kailash 0.2.2__py3-none-any.whl → 0.3.1__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.
- kailash/__init__.py +1 -1
- kailash/access_control.py +40 -39
- kailash/api/auth.py +26 -32
- kailash/api/custom_nodes.py +29 -29
- kailash/api/custom_nodes_secure.py +35 -35
- kailash/api/database.py +17 -17
- kailash/api/gateway.py +19 -19
- kailash/api/mcp_integration.py +24 -23
- kailash/api/studio.py +45 -45
- kailash/api/workflow_api.py +8 -8
- kailash/cli/commands.py +5 -8
- kailash/manifest.py +42 -42
- kailash/mcp/__init__.py +1 -1
- kailash/mcp/ai_registry_server.py +20 -20
- kailash/mcp/client.py +9 -11
- kailash/mcp/client_new.py +10 -10
- kailash/mcp/server.py +1 -2
- kailash/mcp/server_enhanced.py +449 -0
- kailash/mcp/servers/ai_registry.py +6 -6
- kailash/mcp/utils/__init__.py +31 -0
- kailash/mcp/utils/cache.py +267 -0
- kailash/mcp/utils/config.py +263 -0
- kailash/mcp/utils/formatters.py +293 -0
- kailash/mcp/utils/metrics.py +418 -0
- kailash/nodes/ai/agents.py +9 -9
- kailash/nodes/ai/ai_providers.py +33 -34
- kailash/nodes/ai/embedding_generator.py +31 -32
- kailash/nodes/ai/intelligent_agent_orchestrator.py +62 -66
- kailash/nodes/ai/iterative_llm_agent.py +48 -48
- kailash/nodes/ai/llm_agent.py +32 -33
- kailash/nodes/ai/models.py +13 -13
- kailash/nodes/ai/self_organizing.py +44 -44
- kailash/nodes/api/__init__.py +5 -0
- kailash/nodes/api/auth.py +11 -11
- kailash/nodes/api/graphql.py +13 -13
- kailash/nodes/api/http.py +19 -19
- kailash/nodes/api/monitoring.py +463 -0
- kailash/nodes/api/rate_limiting.py +9 -13
- kailash/nodes/api/rest.py +29 -29
- kailash/nodes/api/security.py +819 -0
- kailash/nodes/base.py +24 -26
- kailash/nodes/base_async.py +7 -7
- kailash/nodes/base_cycle_aware.py +12 -12
- kailash/nodes/base_with_acl.py +5 -5
- kailash/nodes/code/python.py +56 -55
- kailash/nodes/data/__init__.py +6 -0
- kailash/nodes/data/directory.py +6 -6
- kailash/nodes/data/event_generation.py +297 -0
- kailash/nodes/data/file_discovery.py +598 -0
- kailash/nodes/data/readers.py +8 -8
- kailash/nodes/data/retrieval.py +10 -10
- kailash/nodes/data/sharepoint_graph.py +17 -17
- kailash/nodes/data/sources.py +5 -5
- kailash/nodes/data/sql.py +13 -13
- kailash/nodes/data/streaming.py +25 -25
- kailash/nodes/data/vector_db.py +22 -22
- kailash/nodes/data/writers.py +7 -7
- kailash/nodes/logic/async_operations.py +17 -17
- kailash/nodes/logic/convergence.py +11 -11
- kailash/nodes/logic/loop.py +4 -4
- kailash/nodes/logic/operations.py +11 -11
- kailash/nodes/logic/workflow.py +8 -9
- kailash/nodes/mixins/mcp.py +17 -17
- kailash/nodes/mixins.py +8 -10
- kailash/nodes/transform/chunkers.py +3 -3
- kailash/nodes/transform/formatters.py +7 -7
- kailash/nodes/transform/processors.py +11 -11
- kailash/runtime/access_controlled.py +18 -18
- kailash/runtime/async_local.py +18 -20
- kailash/runtime/docker.py +24 -26
- kailash/runtime/local.py +55 -31
- kailash/runtime/parallel.py +25 -25
- kailash/runtime/parallel_cyclic.py +29 -29
- kailash/runtime/runner.py +6 -6
- kailash/runtime/testing.py +22 -22
- kailash/sdk_exceptions.py +0 -58
- kailash/security.py +14 -26
- kailash/tracking/manager.py +38 -38
- kailash/tracking/metrics_collector.py +15 -14
- kailash/tracking/models.py +53 -53
- kailash/tracking/storage/base.py +7 -17
- kailash/tracking/storage/database.py +22 -23
- kailash/tracking/storage/filesystem.py +38 -40
- kailash/utils/export.py +21 -21
- kailash/utils/templates.py +8 -9
- kailash/visualization/api.py +30 -34
- kailash/visualization/dashboard.py +17 -17
- kailash/visualization/performance.py +32 -19
- kailash/visualization/reports.py +30 -28
- kailash/workflow/builder.py +8 -8
- kailash/workflow/convergence.py +13 -12
- kailash/workflow/cycle_analyzer.py +38 -33
- kailash/workflow/cycle_builder.py +12 -12
- kailash/workflow/cycle_config.py +16 -15
- kailash/workflow/cycle_debugger.py +40 -40
- kailash/workflow/cycle_exceptions.py +29 -29
- kailash/workflow/cycle_profiler.py +21 -21
- kailash/workflow/cycle_state.py +20 -22
- kailash/workflow/cyclic_runner.py +45 -45
- kailash/workflow/graph.py +57 -45
- kailash/workflow/mermaid_visualizer.py +9 -11
- kailash/workflow/migration.py +22 -22
- kailash/workflow/mock_registry.py +6 -6
- kailash/workflow/runner.py +9 -9
- kailash/workflow/safety.py +12 -13
- kailash/workflow/state.py +8 -11
- kailash/workflow/templates.py +19 -19
- kailash/workflow/validation.py +14 -14
- kailash/workflow/visualization.py +32 -24
- kailash-0.3.1.dist-info/METADATA +476 -0
- kailash-0.3.1.dist-info/RECORD +136 -0
- kailash-0.2.2.dist-info/METADATA +0 -121
- kailash-0.2.2.dist-info/RECORD +0 -126
- {kailash-0.2.2.dist-info → kailash-0.3.1.dist-info}/WHEEL +0 -0
- {kailash-0.2.2.dist-info → kailash-0.3.1.dist-info}/entry_points.txt +0 -0
- {kailash-0.2.2.dist-info → kailash-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.2.2.dist-info → kailash-0.3.1.dist-info}/top_level.txt +0 -0
kailash/runtime/testing.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""Testing utilities for Kailash workflows and nodes."""
|
2
2
|
|
3
3
|
import json
|
4
|
-
from typing import Any
|
4
|
+
from typing import Any
|
5
5
|
|
6
6
|
from kailash.nodes.base import Node, NodeParameter
|
7
7
|
from kailash.runtime.local import LocalRuntime
|
@@ -21,7 +21,7 @@ class MockNode(Node):
|
|
21
21
|
self._fail_message = kwargs.get("fail_message", "Mock failure")
|
22
22
|
self._execution_count = 0
|
23
23
|
|
24
|
-
def get_parameters(self) ->
|
24
|
+
def get_parameters(self) -> dict[str, NodeParameter]:
|
25
25
|
"""Define mock parameters."""
|
26
26
|
return {
|
27
27
|
"input": NodeParameter(
|
@@ -43,7 +43,7 @@ class MockNode(Node):
|
|
43
43
|
),
|
44
44
|
}
|
45
45
|
|
46
|
-
def run(self, **kwargs) ->
|
46
|
+
def run(self, **kwargs) -> dict[str, Any]:
|
47
47
|
"""Execute mock node logic."""
|
48
48
|
self._execution_count += 1
|
49
49
|
|
@@ -64,8 +64,8 @@ class TestDataGenerator:
|
|
64
64
|
|
65
65
|
@staticmethod
|
66
66
|
def generate_csv_data(
|
67
|
-
rows: int = 10, columns:
|
68
|
-
) ->
|
67
|
+
rows: int = 10, columns: list[str] = None
|
68
|
+
) -> list[dict[str, Any]]:
|
69
69
|
"""Generate mock CSV data."""
|
70
70
|
if columns is None:
|
71
71
|
columns = ["id", "name", "value", "category"]
|
@@ -88,7 +88,7 @@ class TestDataGenerator:
|
|
88
88
|
return data
|
89
89
|
|
90
90
|
@staticmethod
|
91
|
-
def generate_json_data(structure: str = "simple") ->
|
91
|
+
def generate_json_data(structure: str = "simple") -> dict | list:
|
92
92
|
"""Generate mock JSON data."""
|
93
93
|
if structure == "simple":
|
94
94
|
return {
|
@@ -154,8 +154,8 @@ class WorkflowTestHelper:
|
|
154
154
|
self,
|
155
155
|
workflow: Workflow,
|
156
156
|
with_tracking: bool = True,
|
157
|
-
parameters:
|
158
|
-
) ->
|
157
|
+
parameters: dict | None = None,
|
158
|
+
) -> tuple[dict[str, Any], str | None]:
|
159
159
|
"""Run a workflow with optional tracking."""
|
160
160
|
if with_tracking:
|
161
161
|
self.task_manager = TaskManager()
|
@@ -165,7 +165,7 @@ class WorkflowTestHelper:
|
|
165
165
|
return self.runtime.execute(workflow, self.task_manager, parameters)
|
166
166
|
|
167
167
|
def assert_workflow_success(
|
168
|
-
self, workflow: Workflow, expected_nodes:
|
168
|
+
self, workflow: Workflow, expected_nodes: list[str] | None = None
|
169
169
|
):
|
170
170
|
"""Assert that a workflow runs successfully."""
|
171
171
|
results, run_id = self.run_workflow(workflow)
|
@@ -182,10 +182,10 @@ class WorkflowTestHelper:
|
|
182
182
|
|
183
183
|
def assert_node_output(
|
184
184
|
self,
|
185
|
-
results:
|
185
|
+
results: dict[str, Any],
|
186
186
|
node_id: str,
|
187
|
-
expected_keys:
|
188
|
-
expected_values:
|
187
|
+
expected_keys: list[str],
|
188
|
+
expected_values: dict | None = None,
|
189
189
|
):
|
190
190
|
"""Assert that a node produced expected output."""
|
191
191
|
assert node_id in results, f"Node {node_id} not found in results"
|
@@ -208,7 +208,7 @@ class NodeTestHelper:
|
|
208
208
|
"""Helper class for testing individual nodes."""
|
209
209
|
|
210
210
|
@staticmethod
|
211
|
-
def test_node_parameters(node: Node, expected_params:
|
211
|
+
def test_node_parameters(node: Node, expected_params: dict[str, type]):
|
212
212
|
"""Test that a node has expected parameters."""
|
213
213
|
params = node.get_parameters()
|
214
214
|
|
@@ -222,19 +222,19 @@ class NodeTestHelper:
|
|
222
222
|
@staticmethod
|
223
223
|
def test_node_execution(
|
224
224
|
node: Node,
|
225
|
-
inputs:
|
226
|
-
expected_keys:
|
225
|
+
inputs: dict[str, Any],
|
226
|
+
expected_keys: list[str],
|
227
227
|
should_fail: bool = False,
|
228
|
-
) ->
|
228
|
+
) -> dict[str, Any]:
|
229
229
|
"""Test node execution with given inputs."""
|
230
230
|
if should_fail:
|
231
231
|
try:
|
232
|
-
result = node.
|
232
|
+
result = node.run(**inputs)
|
233
233
|
assert False, "Node execution should have failed but didn't"
|
234
234
|
except (NodeValidationError, WorkflowExecutionError):
|
235
235
|
return {}
|
236
236
|
else:
|
237
|
-
result = node.
|
237
|
+
result = node.run(**inputs)
|
238
238
|
|
239
239
|
# Check expected output keys
|
240
240
|
for key in expected_keys:
|
@@ -244,7 +244,7 @@ class NodeTestHelper:
|
|
244
244
|
|
245
245
|
@staticmethod
|
246
246
|
def test_node_validation(
|
247
|
-
node: Node, valid_inputs:
|
247
|
+
node: Node, valid_inputs: dict[str, Any], invalid_inputs: list[dict[str, Any]]
|
248
248
|
):
|
249
249
|
"""Test node input validation."""
|
250
250
|
# Test valid inputs
|
@@ -269,7 +269,7 @@ class TestReporter:
|
|
269
269
|
"""Initialize reporter with task manager."""
|
270
270
|
self.task_manager = task_manager
|
271
271
|
|
272
|
-
def generate_run_report(self, run_id: str) ->
|
272
|
+
def generate_run_report(self, run_id: str) -> dict[str, Any]:
|
273
273
|
"""Generate a detailed report for a workflow run."""
|
274
274
|
run = self.task_manager.get_run_summary(run_id)
|
275
275
|
tasks = self.task_manager.list_tasks(run_id)
|
@@ -305,7 +305,7 @@ class TestReporter:
|
|
305
305
|
|
306
306
|
return report
|
307
307
|
|
308
|
-
def save_report(self, report:
|
308
|
+
def save_report(self, report: dict[str, Any], output_path: str):
|
309
309
|
"""Save report to file."""
|
310
310
|
with open(output_path, "w") as f:
|
311
311
|
json.dump(report, f, indent=2, default=str)
|
@@ -325,7 +325,7 @@ def create_test_node(node_type: str = "MockNode", **config) -> Node:
|
|
325
325
|
|
326
326
|
|
327
327
|
def create_test_workflow(
|
328
|
-
name: str = "test_workflow", nodes:
|
328
|
+
name: str = "test_workflow", nodes: list[dict] | None = None
|
329
329
|
) -> Workflow:
|
330
330
|
"""Create a test workflow with specified nodes."""
|
331
331
|
workflow = Workflow(name=name)
|
kailash/sdk_exceptions.py
CHANGED
@@ -77,15 +77,11 @@ See Also:
|
|
77
77
|
class KailashException(Exception):
|
78
78
|
"""Base exception for all Kailash SDK errors."""
|
79
79
|
|
80
|
-
pass
|
81
|
-
|
82
80
|
|
83
81
|
# Node-related exceptions
|
84
82
|
class NodeException(KailashException):
|
85
83
|
"""Base exception for node-related errors."""
|
86
84
|
|
87
|
-
pass
|
88
|
-
|
89
85
|
|
90
86
|
class NodeValidationError(NodeException):
|
91
87
|
"""Raised when node validation fails.
|
@@ -96,8 +92,6 @@ class NodeValidationError(NodeException):
|
|
96
92
|
- Configuration is invalid
|
97
93
|
"""
|
98
94
|
|
99
|
-
pass
|
100
|
-
|
101
95
|
|
102
96
|
class NodeExecutionError(NodeException):
|
103
97
|
"""Raised when node execution fails.
|
@@ -108,8 +102,6 @@ class NodeExecutionError(NodeException):
|
|
108
102
|
- Data transformation fails
|
109
103
|
"""
|
110
104
|
|
111
|
-
pass
|
112
|
-
|
113
105
|
|
114
106
|
class NodeConfigurationError(NodeException):
|
115
107
|
"""Raised when node configuration is invalid.
|
@@ -120,8 +112,6 @@ class NodeConfigurationError(NodeException):
|
|
120
112
|
- Required environment variables are missing
|
121
113
|
"""
|
122
114
|
|
123
|
-
pass
|
124
|
-
|
125
115
|
|
126
116
|
class SafetyViolationError(NodeException):
|
127
117
|
"""Raised when code safety validation fails.
|
@@ -132,15 +122,11 @@ class SafetyViolationError(NodeException):
|
|
132
122
|
- Malicious code patterns are detected
|
133
123
|
"""
|
134
124
|
|
135
|
-
pass
|
136
|
-
|
137
125
|
|
138
126
|
# Workflow-related exceptions
|
139
127
|
class WorkflowException(KailashException):
|
140
128
|
"""Base exception for workflow-related errors."""
|
141
129
|
|
142
|
-
pass
|
143
|
-
|
144
130
|
|
145
131
|
class WorkflowValidationError(WorkflowException):
|
146
132
|
"""Raised when workflow validation fails.
|
@@ -151,8 +137,6 @@ class WorkflowValidationError(WorkflowException):
|
|
151
137
|
- Workflow structure is invalid
|
152
138
|
"""
|
153
139
|
|
154
|
-
pass
|
155
|
-
|
156
140
|
|
157
141
|
class WorkflowExecutionError(WorkflowException):
|
158
142
|
"""Raised when workflow execution fails.
|
@@ -163,8 +147,6 @@ class WorkflowExecutionError(WorkflowException):
|
|
163
147
|
- Runtime resources are exhausted
|
164
148
|
"""
|
165
149
|
|
166
|
-
pass
|
167
|
-
|
168
150
|
|
169
151
|
class CyclicDependencyError(WorkflowException):
|
170
152
|
"""Raised when a cyclic dependency is detected in the workflow graph.
|
@@ -173,8 +155,6 @@ class CyclicDependencyError(WorkflowException):
|
|
173
155
|
making it impossible to determine execution order.
|
174
156
|
"""
|
175
157
|
|
176
|
-
pass
|
177
|
-
|
178
158
|
|
179
159
|
class ConnectionError(WorkflowException):
|
180
160
|
"""Raised when node connections are invalid.
|
@@ -185,8 +165,6 @@ class ConnectionError(WorkflowException):
|
|
185
165
|
- Node not found in workflow
|
186
166
|
"""
|
187
167
|
|
188
|
-
pass
|
189
|
-
|
190
168
|
|
191
169
|
class CycleConfigurationError(WorkflowException):
|
192
170
|
"""Raised when cycle configuration is invalid.
|
@@ -202,15 +180,11 @@ class CycleConfigurationError(WorkflowException):
|
|
202
180
|
- Missing source/target nodes before build()
|
203
181
|
"""
|
204
182
|
|
205
|
-
pass
|
206
|
-
|
207
183
|
|
208
184
|
# Runtime-related exceptions
|
209
185
|
class RuntimeException(KailashException):
|
210
186
|
"""Base exception for runtime-related errors."""
|
211
187
|
|
212
|
-
pass
|
213
|
-
|
214
188
|
|
215
189
|
class RuntimeExecutionError(RuntimeException):
|
216
190
|
"""Raised when runtime execution fails.
|
@@ -221,15 +195,11 @@ class RuntimeExecutionError(RuntimeException):
|
|
221
195
|
- Execution is interrupted
|
222
196
|
"""
|
223
197
|
|
224
|
-
pass
|
225
|
-
|
226
198
|
|
227
199
|
# Task tracking exceptions
|
228
200
|
class TaskException(KailashException):
|
229
201
|
"""Base exception for task tracking errors."""
|
230
202
|
|
231
|
-
pass
|
232
|
-
|
233
203
|
|
234
204
|
class TaskStateError(TaskException):
|
235
205
|
"""Raised when task state operations fail.
|
@@ -240,15 +210,11 @@ class TaskStateError(TaskException):
|
|
240
210
|
- Concurrent modification conflicts occur
|
241
211
|
"""
|
242
212
|
|
243
|
-
pass
|
244
|
-
|
245
213
|
|
246
214
|
# Storage exceptions
|
247
215
|
class StorageException(KailashException):
|
248
216
|
"""Base exception for storage-related errors."""
|
249
217
|
|
250
|
-
pass
|
251
|
-
|
252
218
|
|
253
219
|
class KailashStorageError(StorageException):
|
254
220
|
"""Raised when storage operations fail.
|
@@ -260,8 +226,6 @@ class KailashStorageError(StorageException):
|
|
260
226
|
- Data formatting is incorrect
|
261
227
|
"""
|
262
228
|
|
263
|
-
pass
|
264
|
-
|
265
229
|
|
266
230
|
# Import/Export exceptions
|
267
231
|
class ExportException(KailashException):
|
@@ -273,8 +237,6 @@ class ExportException(KailashException):
|
|
273
237
|
- Serialization fails
|
274
238
|
"""
|
275
239
|
|
276
|
-
pass
|
277
|
-
|
278
240
|
|
279
241
|
class ImportException(KailashException):
|
280
242
|
"""Raised when import operations fail.
|
@@ -285,8 +247,6 @@ class ImportException(KailashException):
|
|
285
247
|
- Deserialization fails
|
286
248
|
"""
|
287
249
|
|
288
|
-
pass
|
289
|
-
|
290
250
|
|
291
251
|
# Configuration exceptions
|
292
252
|
class ConfigurationException(KailashException):
|
@@ -298,8 +258,6 @@ class ConfigurationException(KailashException):
|
|
298
258
|
- Configuration schema is invalid
|
299
259
|
"""
|
300
260
|
|
301
|
-
pass
|
302
|
-
|
303
261
|
|
304
262
|
class KailashConfigError(ConfigurationException):
|
305
263
|
"""Raised when configuration is invalid (legacy name).
|
@@ -307,8 +265,6 @@ class KailashConfigError(ConfigurationException):
|
|
307
265
|
This is an alias for ConfigurationException for backward compatibility.
|
308
266
|
"""
|
309
267
|
|
310
|
-
pass
|
311
|
-
|
312
268
|
|
313
269
|
# Manifest exceptions
|
314
270
|
class ManifestError(KailashException):
|
@@ -320,8 +276,6 @@ class ManifestError(KailashException):
|
|
320
276
|
- Version incompatibility
|
321
277
|
"""
|
322
278
|
|
323
|
-
pass
|
324
|
-
|
325
279
|
|
326
280
|
# CLI exceptions
|
327
281
|
class CLIException(KailashException):
|
@@ -333,8 +287,6 @@ class CLIException(KailashException):
|
|
333
287
|
- Command execution fails
|
334
288
|
"""
|
335
289
|
|
336
|
-
pass
|
337
|
-
|
338
290
|
|
339
291
|
# Visualization exceptions
|
340
292
|
class VisualizationError(KailashException):
|
@@ -346,8 +298,6 @@ class VisualizationError(KailashException):
|
|
346
298
|
- Output format is unsupported
|
347
299
|
"""
|
348
300
|
|
349
|
-
pass
|
350
|
-
|
351
301
|
|
352
302
|
# Template exceptions
|
353
303
|
class TemplateError(KailashException):
|
@@ -359,8 +309,6 @@ class TemplateError(KailashException):
|
|
359
309
|
- Variable substitution fails
|
360
310
|
"""
|
361
311
|
|
362
|
-
pass
|
363
|
-
|
364
312
|
|
365
313
|
# Code execution exceptions
|
366
314
|
# (SafetyViolationError already defined above - removing duplicate)
|
@@ -375,8 +323,6 @@ class CodeExecutionError(NodeException):
|
|
375
323
|
- Import or dependency issues
|
376
324
|
"""
|
377
325
|
|
378
|
-
pass
|
379
|
-
|
380
326
|
|
381
327
|
# Resource exceptions
|
382
328
|
class KailashNotFoundException(KailashException):
|
@@ -388,8 +334,6 @@ class KailashNotFoundException(KailashException):
|
|
388
334
|
- A file or resource is missing
|
389
335
|
"""
|
390
336
|
|
391
|
-
pass
|
392
|
-
|
393
337
|
|
394
338
|
# Workflow-specific exceptions
|
395
339
|
class KailashWorkflowException(WorkflowException):
|
@@ -398,8 +342,6 @@ class KailashWorkflowException(WorkflowException):
|
|
398
342
|
This is an alias for WorkflowException for consistency.
|
399
343
|
"""
|
400
344
|
|
401
|
-
pass
|
402
|
-
|
403
345
|
|
404
346
|
# Legacy exception name compatibility for tests and backwards compatibility
|
405
347
|
KailashRuntimeError = RuntimeExecutionError
|
kailash/security.py
CHANGED
@@ -118,7 +118,7 @@ import tempfile
|
|
118
118
|
import time
|
119
119
|
from contextlib import contextmanager
|
120
120
|
from pathlib import Path
|
121
|
-
from typing import Any
|
121
|
+
from typing import Any
|
122
122
|
|
123
123
|
logger = logging.getLogger(__name__)
|
124
124
|
|
@@ -126,43 +126,33 @@ logger = logging.getLogger(__name__)
|
|
126
126
|
class SecurityError(Exception):
|
127
127
|
"""Raised when a security policy violation is detected."""
|
128
128
|
|
129
|
-
pass
|
130
|
-
|
131
129
|
|
132
130
|
class PathTraversalError(SecurityError):
|
133
131
|
"""Raised when path traversal attempt is detected."""
|
134
132
|
|
135
|
-
pass
|
136
|
-
|
137
133
|
|
138
134
|
class CommandInjectionError(SecurityError):
|
139
135
|
"""Raised when command injection attempt is detected."""
|
140
136
|
|
141
|
-
pass
|
142
|
-
|
143
137
|
|
144
138
|
class ExecutionTimeoutError(SecurityError):
|
145
139
|
"""Raised when execution exceeds allowed time limit."""
|
146
140
|
|
147
|
-
pass
|
148
|
-
|
149
141
|
|
150
142
|
class MemoryLimitError(SecurityError):
|
151
143
|
"""Raised when memory usage exceeds allowed limit."""
|
152
144
|
|
153
|
-
pass
|
154
|
-
|
155
145
|
|
156
146
|
class SecurityConfig:
|
157
147
|
"""Configuration for security policies and limits."""
|
158
148
|
|
159
149
|
def __init__(
|
160
150
|
self,
|
161
|
-
allowed_directories:
|
151
|
+
allowed_directories: list[str] | None = None,
|
162
152
|
max_file_size: int = 100 * 1024 * 1024, # 100MB
|
163
153
|
execution_timeout: float = 300.0, # 5 minutes
|
164
154
|
memory_limit: int = 512 * 1024 * 1024, # 512MB
|
165
|
-
allowed_file_extensions:
|
155
|
+
allowed_file_extensions: list[str] | None = None,
|
166
156
|
enable_audit_logging: bool = True,
|
167
157
|
enable_path_validation: bool = True,
|
168
158
|
enable_command_validation: bool = True,
|
@@ -242,8 +232,8 @@ def set_security_config(config: SecurityConfig) -> None:
|
|
242
232
|
|
243
233
|
|
244
234
|
def validate_file_path(
|
245
|
-
file_path:
|
246
|
-
config:
|
235
|
+
file_path: str | Path,
|
236
|
+
config: SecurityConfig | None = None,
|
247
237
|
operation: str = "access",
|
248
238
|
) -> Path:
|
249
239
|
"""
|
@@ -343,9 +333,9 @@ def validate_file_path(
|
|
343
333
|
|
344
334
|
|
345
335
|
def safe_open(
|
346
|
-
file_path:
|
336
|
+
file_path: str | Path,
|
347
337
|
mode: str = "r",
|
348
|
-
config:
|
338
|
+
config: SecurityConfig | None = None,
|
349
339
|
**kwargs,
|
350
340
|
):
|
351
341
|
"""
|
@@ -391,9 +381,7 @@ def safe_open(
|
|
391
381
|
return open(validated_path, mode, **kwargs)
|
392
382
|
|
393
383
|
|
394
|
-
def validate_command_string(
|
395
|
-
command: str, config: Optional[SecurityConfig] = None
|
396
|
-
) -> str:
|
384
|
+
def validate_command_string(command: str, config: SecurityConfig | None = None) -> str:
|
397
385
|
"""
|
398
386
|
Validate command strings to prevent injection attacks.
|
399
387
|
|
@@ -445,7 +433,7 @@ def validate_command_string(
|
|
445
433
|
|
446
434
|
@contextmanager
|
447
435
|
def execution_timeout(
|
448
|
-
timeout:
|
436
|
+
timeout: float | None = None, config: SecurityConfig | None = None
|
449
437
|
):
|
450
438
|
"""
|
451
439
|
Context manager to enforce execution timeouts.
|
@@ -485,8 +473,8 @@ def execution_timeout(
|
|
485
473
|
def sanitize_input(
|
486
474
|
value: Any,
|
487
475
|
max_length: int = 10000,
|
488
|
-
allowed_types:
|
489
|
-
config:
|
476
|
+
allowed_types: list[type] | None = None,
|
477
|
+
config: SecurityConfig | None = None,
|
490
478
|
) -> Any:
|
491
479
|
"""
|
492
480
|
Sanitize input values to prevent injection attacks.
|
@@ -776,7 +764,7 @@ def sanitize_input(
|
|
776
764
|
|
777
765
|
|
778
766
|
def create_secure_temp_dir(
|
779
|
-
prefix: str = "kailash_", config:
|
767
|
+
prefix: str = "kailash_", config: SecurityConfig | None = None
|
780
768
|
) -> Path:
|
781
769
|
"""
|
782
770
|
Create a secure temporary directory.
|
@@ -804,8 +792,8 @@ def create_secure_temp_dir(
|
|
804
792
|
|
805
793
|
|
806
794
|
def validate_node_parameters(
|
807
|
-
parameters:
|
808
|
-
) ->
|
795
|
+
parameters: dict[str, Any], config: SecurityConfig | None = None
|
796
|
+
) -> dict[str, Any]:
|
809
797
|
"""
|
810
798
|
Validate and sanitize node parameters.
|
811
799
|
|