{"record":{"id":"1cdb0a04d9e975cb","repo":"n8n-io/n8n","slug":"security-violations-detected","errorCode":null,"errorMessage":"Security violations detected","messagePattern":"Security violations detected","errorType":"exception","errorClass":"SecurityViolationError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/task-runner-python/src/task_analyzer.py","lineNumber":363,"sourceCode":"            self._cache.move_to_end(cache_key)\n\n            if len(cached_violations) == 0:\n                return\n\n            self._raise_security_error(cached_violations)\n\n        tree = ast.parse(code)\n\n        security_validator = SecurityValidator(self._security_config)\n        security_validator.visit(tree)\n\n        self._set_in_cache(cache_key, security_validator.violations)\n\n        if security_validator.violations:\n            self._raise_security_error(security_validator.violations)\n\n    def _raise_security_error(self, violations: CachedViolations) -> None:\n        raise SecurityViolationError(\n            message=\"Security violations detected\", description=\"\\n\".join(violations)\n        )\n\n    def _to_cache_key(self, code: str) -> CacheKey:\n        code_hash = hashlib.sha256(code.encode()).hexdigest()\n        return (code_hash, self._allowlists)\n\n    def _set_in_cache(self, cache_key: CacheKey, violations: CachedViolations) -> None:\n        if len(self._cache) >= MAX_VALIDATION_CACHE_SIZE:\n            self._cache.popitem(last=False)  # FIFO\n\n        self._cache[cache_key] = violations.copy()\n        self._cache.move_to_end(cache_key)\n","sourceCodeStart":345,"sourceCodeEnd":377,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/task-runner-python/src/task_analyzer.py#L345-L377","documentation":"Thrown by TaskAnalyzer.validate when the AST-based static security scan of Python user code detects one or more security violations. The SecurityValidator walks the parsed AST and collects violation descriptions; if any are found, they are joined with newlines and passed as the description of a SecurityViolationError. This is a pre-execution static analysis pass that catches dangerous patterns before the code runs in the subprocess.","triggerScenarios":"Python Code Node code is submitted for execution. TaskAnalyzer.validate parses it into an AST, runs SecurityValidator.visit(tree), and the validator flags constructs like dangerous attribute access, use of eval/exec, access to dunder attributes, or calls to forbidden functions. The violations list is non-empty, triggering _raise_security_error.","commonSituations":"User code accesses __import__, __builtins__, or other dunder attributes to bypass sandboxing. Code uses eval(), exec(), compile(), or getattr for dynamic attribute access on sensitive objects. AST patterns matching known escape vectors from the sandbox. The allowlist is restrictive and the code uses introspection or metaprogramming.","solutions":["Read the SecurityViolationError description to see which specific patterns were flagged.","Remove or replace dangerous constructs: use direct imports instead of __import__, avoid eval/exec.","If the flagged construct is legitimate, refactor to an explicitly allowed alternative.","Adjust the security allowlist if the code is trusted and the pattern is a false positive (consult your security policy first)."],"exampleFix":"# before — flagged by analyzer\nimport os\nos.system('rm -rf /tmp/test')\ngetattr(os, 'sys' + 'tem')('whoami')\n\n# after — use allowed APIs\n# Use the HTTP Request node or an allowed module instead\nimport json\nresult = json.dumps({'key': 'value'})","handlingStrategy":"validation","validationCode":"import ast\n\ndef pre_validate_code(code: str) -> list[str]:\n    \"\"\"Check for obvious security violations before submission.\"\"\"\n    violations = []\n    tree = ast.parse(code)\n    for node in ast.walk(tree):\n        if isinstance(node, ast.Call):\n            if isinstance(node.func, ast.Name) and node.func.id in ('eval', 'exec', 'compile'):\n                violations.append(f'Use of {node.func.id}() is not allowed')\n        if isinstance(node, ast.Attribute) and node.attr.startswith('_'):\n            violations.append(f'Access to {node.attr} is not allowed')\n    return violations\n\nissues = pre_validate_code(user_code)\nif issues:\n    raise ValueError('Security issues: ' + '; '.join(issues))","typeGuard":null,"tryCatchPattern":"from task_analyzer import TaskAnalyzer\nfrom _sandbox_callables import SecurityViolationError\n\ntry:\n    analyzer.validate(code)\nexcept SecurityViolationError as e:\n    print(f'Security violations: {e.description}')\n    # show violations to user for correction","preventionTips":["Avoid using eval(), exec(), compile(), or __import__() in Code Node Python.","Don't access dunder attributes (__class__, __mro__, __subclasses__) in user code.","Pre-validate user code with a local AST check before submitting to the runner.","Educate users on sandbox restrictions and supported APIs."],"tags":["task-runner","python","security","sandbox","static-analysis","code-node"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}