{"record":{"id":"4ff10fcdefeef400","repo":"huggingface/smolagents","slug":"forbidden-access-to-dunder-attribute-expression","errorCode":null,"errorMessage":"Forbidden access to dunder attribute: {expression.attr}","messagePattern":"Forbidden access to dunder attribute: (.+?)","errorType":"error_code","errorClass":"InterpreterError","httpStatus":null,"severity":"error","filePath":"src/smolagents/local_python_executor.py","lineNumber":391,"sourceCode":"    current_node = build_import_tree(authorized_imports)\n    for part in import_to_check.split(\".\"):\n        if \"*\" in current_node:\n            return True\n        if part not in current_node:\n            return False\n        current_node = current_node[part]\n    return True\n\n\ndef evaluate_attribute(\n    expression: ast.Attribute,\n    state: dict[str, Any],\n    static_tools: dict[str, Callable],\n    custom_tools: dict[str, Callable],\n    authorized_imports: list[str],\n) -> Any:\n    if expression.attr.startswith(\"__\") and expression.attr.endswith(\"__\"):\n        raise InterpreterError(f\"Forbidden access to dunder attribute: {expression.attr}\")\n    value = evaluate_ast(expression.value, state, static_tools, custom_tools, authorized_imports)\n    return getattr(value, expression.attr)\n\n\ndef evaluate_unaryop(\n    expression: ast.UnaryOp,\n    state: dict[str, Any],\n    static_tools: dict[str, Callable],\n    custom_tools: dict[str, Callable],\n    authorized_imports: list[str],\n) -> Any:\n    operand = evaluate_ast(expression.operand, state, static_tools, custom_tools, authorized_imports)\n    if isinstance(expression.op, ast.USub):\n        return -operand\n    elif isinstance(expression.op, ast.UAdd):\n        return operand\n    elif isinstance(expression.op, ast.Not):\n        return not operand","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/huggingface/smolagents/blob/30bb1161095dbae2271e6bc3cc4c219cc3897a57/src/smolagents/local_python_executor.py#L373-L409","documentation":"While evaluating attribute access (obj.attr), the executor rejects any attribute whose name both starts and ends with '__' (dunder attributes). This blocks sandbox escapes like obj.__class__.__bases__[0].__subclasses__() or func.__globals__ that use dunders to reach privileged machinery.","triggerScenarios":"Generated code accesses a dunder attribute: `x.__class__`, `f.__globals__`, `obj.__dict__`, `instance.__init__`, etc. The check fires before the value is even evaluated.","commonSituations":"LLM tries introspection to escape the sandbox; benign-but-fancy code uses __class__ to check types or __name__ for logging; serialization helpers reaching for __dict__.","solutions":["Replace dunder introspection with safe equivalents: type(x) instead of x.__class__, getattr-free public attributes, vars(x) where allowed","Use isinstance checks for type detection instead of __class__ chains","If you control the code, remove any __-wrapped attribute access entirely — there is no whitelist option in the executor"],"exampleFix":"# before\ncode = \"final_answer(item.__class__.__name__)\"\n\n# after\ncode = \"final_answer(type(item).__name__)\"","handlingStrategy":"validation","validationCode":"import ast\nfor node in ast.walk(ast.parse(code)):\n    if isinstance(node, ast.Attribute) and node.attr.startswith('__') and node.attr.endswith('__'):\n        raise ValueError(f'dunder access not allowed: {node.attr} (line {node.lineno})')","typeGuard":null,"tryCatchPattern":"from smolagents.local_python_executor import InterpreterError\ntry:\n    evaluate_python(code)\nexcept InterpreterError as e:\n    if 'dunder attribute' in str(e):\n        code = code.replace('.__class__', ' type(')  # rewrite to safe equivalents","preventionTips":["Use type(x) instead of x.__class__ in generated code","Avoid __dict__/__globals__/__name__ in snippets destined for the sandbox","Lint generated code for dunder attribute access before execution"],"tags":["smolagents","sandbox","dunder","security","attribute-access"],"backgroundTag":"sandbox-escape-blocked","analyzedSha":"30bb1161095dbae2271e6bc3cc4c219cc3897a57","analyzedAt":"2026-08-28T18:52:54.169Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}