T09 · Insecure Skill Coding Practices
- Location
scripts/csv_filter.py:29- Finding
Arbitrary Python Code Execution Through Unsafe Filter Evaluation
- Content
View full analysis
Vulnerability Details
File Location:
scripts/csv_filter.py, lines 29-47
Vulnerability Type: Unsafe evaluation of user-controlled expressions
Risk Level: HighVulnerable Code
python if args.where: filtered = [] for row in data: env = {k: v for k, v in row.items()} # Try numeric conversion for comparison for k, v in env.items(): try: env[k] = int(v) except ValueError: try: env[k] = float(v) except ValueError: pass try: if eval(args.where, {"__builtins__": {}}, env): filtered.append(row) except Exception as e: print(f"Filter error: {e}") sys.exit(1) data = filteredTechnical Analysis
The value supplied through the
--wherecommand-line argument is passed directly to Python'seval(). Although the global namespace replaces__builtins__with an empty dictionary, this does not create a secure sandbox.Python expressions can traverse the object model through attributes such as class metadata, base classes, subclasses, and function global namespaces. Depending on classes already loaded in the interpreter, an attacker may recover access to built-in functions or other runtime capabilities. This can permit file access, module loading, or operating-system command execution.
The documented interface encourages users or an AI Agent to place filter expressions directly in
--where. Therefore, an attacker who influences that expression can cross the boundary from data filtering into arbitrary Python execution.Attack Path
- An attacker supplies or recommends a crafted value for the
--whereargument. - A user or AI Agent invokes
csv_filter.pywith that expression. - The script passes the expression to
eval()for every CSV row. - The express ...[truncated 926 chars]
- An attacker supplies or recommends a crafted value for the
- Remediation
View remediation
Remediation Suggestions
Remove
eval()entirely and implement an allowlisted expression parser.- Parse the expression with
ast.parse()in expression mode or use a dedicated filtering grammar. - Permit only the required syntax, such as:
- Boolean operations:
and,or, andnot - Comparisons: equality, inequality, and ordered comparisons
- Column identifiers
- String and numeric literals
- Boolean operations:
- Explicitly reject function calls, attribute access, subscripting, comprehensions, lambda expressions, imports, and all other AST node types.
- Validate every identifier against the CSV column names.
- Evaluate the validated syntax through custom comparison logic rather than compiling and executing it as Python.
- Add tests containing object-traversal and function-call payloads to ensure they are rejected.
- If a full expression language is unnecessary, replace
--wherewith structured arguments such as--column,--operator, and--value.
- Parse the expression with
