Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import ast | |
| BLOCKED_NODES = ( | |
| ast.Import, | |
| ast.ImportFrom, | |
| ast.Global, | |
| ast.Nonlocal, | |
| ast.With, | |
| ast.AsyncWith, | |
| ast.Try, | |
| ast.Raise, | |
| ast.ClassDef, | |
| ast.FunctionDef, | |
| ast.AsyncFunctionDef, | |
| ) | |
| BLOCKED_CALLS = { | |
| "eval", | |
| "exec", | |
| "compile", | |
| "open", | |
| "input", | |
| "__import__", | |
| "breakpoint", | |
| "getattr", | |
| "setattr", | |
| "delattr", | |
| } | |
| class UnsafeCodeError(ValueError): | |
| pass | |
| def validate_generated_expression(expression: str, max_characters: int = 2_000) -> ast.Expression: | |
| """Validate a calculation expression before it is sent to an isolated worker. | |
| DataPilot's primary workflow does not execute LLM-generated Python. This validator | |
| exists for an optional restricted calculation worker and accepts expressions only. | |
| """ | |
| if len(expression) > max_characters: | |
| raise UnsafeCodeError("Expression exceeds the configured size limit.") | |
| try: | |
| tree = ast.parse(expression, mode="eval") | |
| except SyntaxError as exc: | |
| raise UnsafeCodeError("Expression is not valid Python.") from exc | |
| for node in ast.walk(tree): | |
| if isinstance(node, BLOCKED_NODES): | |
| raise UnsafeCodeError(f"Blocked syntax: {type(node).__name__}.") | |
| if isinstance(node, ast.Attribute) and node.attr.startswith("__"): | |
| raise UnsafeCodeError("Dunder attribute access is blocked.") | |
| if isinstance(node, ast.Name) and node.id.startswith("__"): | |
| raise UnsafeCodeError("Dunder names are blocked.") | |
| if isinstance(node, ast.Call): | |
| if isinstance(node.func, ast.Name) and node.func.id in BLOCKED_CALLS: | |
| raise UnsafeCodeError(f"Blocked call: {node.func.id}.") | |
| if isinstance(node.func, ast.Attribute) and node.func.attr in BLOCKED_CALLS: | |
| raise UnsafeCodeError(f"Blocked call: {node.func.attr}.") | |
| return tree | |