diff --git a/packages/components/src/pythonCodeValidator.ts b/packages/components/src/pythonCodeValidator.ts index 6cd77aea5c1..4c93959bb93 100644 --- a/packages/components/src/pythonCodeValidator.ts +++ b/packages/components/src/pythonCodeValidator.ts @@ -68,6 +68,8 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [ { pattern: /\bdir\s*\(/g, reason: 'dir()' }, { pattern: /\b__dict__\b/g, reason: '__dict__ (attribute reflection)' }, { pattern: /\b__module__\b/g, reason: '__module__ (module reflection)' }, + { pattern: /\b__getattribute__\b/g, reason: '__getattribute__ (attribute access bypass)' }, + { pattern: /\b__getattr__\b/g, reason: '__getattr__ (attribute access bypass)' }, // Unsafe deserialization — read_pickle() executes arbitrary Python objects { pattern: /\bread_pickle\b/g, reason: 'read_pickle (unsafe deserialization / RCE)' }, { pattern: /\bpickle\b/g, reason: 'pickle module (unsafe deserialization)' }, diff --git a/tests/invariant_pythonCodeValidator.test.ts b/tests/invariant_pythonCodeValidator.test.ts new file mode 100644 index 00000000000..2b93e5ec3d7 --- /dev/null +++ b/tests/invariant_pythonCodeValidator.test.ts @@ -0,0 +1,21 @@ +import { validatePythonCodeForDataFrame } from "../../../packages/components/src/pythonCodeValidator"; + +describe("Python code validator blocks all known RCE bypass patterns", () => { + const payloads: Array<[string, string, boolean]> = [ + // Exact exploit: chained attribute access to reach os.system + ["chained attribute os.system via pd.io", "import pandas as pd\nresult = pd.io.common.os.system('id')", false], + // Nested function hiding import + ["nested def hiding __import__", "def outer():\n def inner():\n __import__('os').system('id')\n inner()\nouter()", false], + // eval/exec wrapper bypass + ["eval wrapping exec", "eval(\"exec('import os; os.system(\\\"id\\\")')\")", false], + // compile() abuse + ["compile() abuse", "exec(compile('import os\\nos.system(\"id\")', '', 'exec'))", false], + // Valid safe input — must be accepted + ["valid dataframe operation", "df = df[df['value'] > 0]\nresult = df.groupby('category').sum()", true], + ]; + + test.each(payloads)("%s", (_label, code, shouldBeValid) => { + const result = validatePythonCodeForDataFrame(code); + expect(result.valid).toBe(shouldBeValid); + }); +});