1515from datetime import datetime
1616import hashlib
1717
18+ import re
1819# Optional imports for advanced features
1920try :
2021 import plotly .express as px
@@ -298,6 +299,28 @@ def create_quality_filtered_dataset(self, dataset_id: str, min_trust_score: floa
298299 self .logger .error (f"Error creating quality-filtered dataset: { e } " )
299300 raise
300301
302+ def is_safe_query (self , query_str : str , allowed_columns ) -> bool :
303+ """
304+ Check if the query string is safe: only allowed column names, numbers, and safe operators.
305+ """
306+ # Only allow column names, numbers, whitespace, and safe operators
307+ # Disallow parentheses, function calls, __import__, etc.
308+ # Allowed operators: ==, !=, <, >, <=, >=, and, or, not
309+ # Build regex for allowed columns
310+ col_pattern = r'|' .join ([re .escape (col ) for col in allowed_columns ])
311+ # Full pattern: allowed columns, numbers, operators, whitespace
312+ safe_pattern = rf'^([\s\d\.\'"]*({ col_pattern } )[\s\d\.\'"]*(==|!=|<=|>=|<|>|and|or|not|&|\||\s)*[\s\d\.\'"]*)+$'
313+ # Disallow suspicious keywords
314+ forbidden = ['__import__' , 'os.' , 'sys.' , 'eval' , 'exec' , 'open(' , '(' , ')' , '[' , ']' , '{' , '}' , ';' ]
315+ lowered = query_str .lower ()
316+ for word in forbidden :
317+ if word in lowered :
318+ return False
319+ # Check regex
320+ if re .match (safe_pattern , query_str ):
321+ return True
322+ return False
323+
301324 def process_dataset (self , dataset_id : str , transformations : List [Dict ]) -> str :
302325 """
303326 Apply transformations to a dataset
@@ -322,7 +345,10 @@ def process_dataset(self, dataset_id: str, transformations: List[Dict]) -> str:
322345 elif operation == 'rename_columns' :
323346 df = df .rename (columns = params ['mapping' ])
324347 elif operation == 'filter' :
325- df = df .query (params ['condition' ])
348+ condition = params ['condition' ]
349+ if not self .is_safe_query (condition , df .columns ):
350+ raise ValueError ("Unsafe filter condition detected. Only simple column comparisons are allowed." )
351+ df = df .query (condition )
326352 elif operation == 'sort' :
327353 df = df .sort_values (by = params ['columns' ], ascending = params .get ('ascending' , True ))
328354 elif operation == 'groupby' :
0 commit comments