{"record":{"id":"62cb8232b82d919b","repo":"n8n-io/n8n","slug":"string-pattern-accessing-attr-is-disallowed-b","errorCode":null,"errorMessage":"String pattern accessing '{attr}' is disallowed, because it can be used to bypass security restrictions.","messagePattern":"String pattern accessing '(.+?)' is disallowed, because it can be used to bypass security restrictions\\.","errorType":"exception","errorClass":"SecurityViolationError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/task-runner-python/src/task_executor.py","lineNumber":93,"sourceCode":"        ):\n            replacement = ast.Call(\n                func=ast.Name(id=EXECUTOR_SAFE_FORMAT_KEY, ctx=ast.Load()),\n                args=[\n                    ast.Constant(value=node.func.attr),\n                    node.func.value,\n                    *node.args,\n                ],\n                keywords=node.keywords,\n            )\n            return ast.copy_location(replacement, node)\n\n        return node\n\n\ndef _validate_format_template(template: str) -> None:\n    token = next(find_blocked_format_tokens(template), None)\n    if token is not None:\n        raise SecurityViolationError(\n            description=ERROR_DANGEROUS_STRING_PATTERN.format(attr=token),\n        )\n\n\ndef _validate_field_expression(expr: str) -> None:\n    # Wrap as a complete template so the existing parser can scan it.\n    _validate_format_template(\"{\" + expr + \"}\")\n\n\n_TEMPLATE_METHODS = frozenset({\"format\", \"format_map\", \"vformat\"})\n_FIELD_METHODS = frozenset({\"get_field\"})\n\n\ndef _resolve_template_arg(method_name: str, receiver, args):\n    \"\"\"Return ``(template, field)`` for the call: at most one element is\n    non-``None``. Normalises bound and unbound call forms so the same\n    validation runs for both ``\"tpl\".format(...)``, ``str.format(\"tpl\", ...)``,\n    ``Formatter().format(\"tpl\", ...)``, and ``Formatter.format(f, \"tpl\", ...)``.","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/task-runner-python/src/task_executor.py#L75-L111","documentation":"Thrown by _validate_format_template in the Python task executor when a str.format, str.format_map, or f-string-equivalent template contains a field access pattern that the security scanner considers dangerous. The find_blocked_format_tokens generator scans the format template for attribute chains (e.g. {0.__class__.__init__}) that could be used to traverse the Python object model and bypass sandbox restrictions. This blocks format-string attacks at execution time.","triggerScenarios":"Python user code calls '{0.__class__}'.format(obj), '{0.__class__.__mro__}'.format(obj), obj.format('{x.__init__}', x=obj), or any string formatting that uses dot-access to reach dunder or internal attributes. The FormatGuardTransformer rewrites format calls to route through _validate_format_template, which scans the template and finds a blocked token.","commonSituations":"Using format strings with attribute access for legitimate introspection that happens to touch blocked attributes. Attempting format-string-based sandbox escape by walking __class__.__mro__.__subclasses__(). Dynamic template generation from user-controlled input that includes attribute paths.","solutions":["Avoid attribute access in format strings — use positional or named fields without dots: '{name}' instead of '{obj.name}'.","Pre-compute any values you need and pass them as simple arguments rather than traversing objects in the template.","If you need object attribute access, do it in regular Python code before formatting, not inside the template string.","Review ERROR_DANGEROUS_STRING_PATTERN to understand which tokens are blocked."],"exampleFix":"# before — blocked attribute traversal in format string\nresult = '{0.__class__.__name__}'.format(my_object)\n\n# after — compute the value separately\nresult = type(my_object).__name__\ntemplate = '{}'.format(result)","handlingStrategy":"validation","validationCode":"import re\n\nBLOCKED_PATTERN = re.compile(r'\\{[^}]*\\.__\\w+')\n\ndef is_safe_format_template(template: str) -> bool:\n    return not BLOCKED_PATTERN.search(template)\n\n# check before using\ntemplate = '{0.__class__}'\nif not is_safe_format_template(template):\n    raise ValueError('Format template contains blocked attribute access')","typeGuard":null,"tryCatchPattern":"from task_executor import SecurityViolationError\n\ntry:\n    result = my_template.format(obj)\nexcept SecurityViolationError as e:\n    # rewrite the template to avoid attribute access\n    name = type(obj).__name__\n    result = f'{name}'  # use pre-computed values","preventionTips":["Never use attribute access (dots) in format string templates.","Pre-compute values in regular code and pass them as simple arguments to format().","Avoid user-controlled input in format string templates.","Use f-strings or % formatting with simple values instead of complex format templates."],"tags":["task-runner","python","security","sandbox","format-string","code-node"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}