Please don't use eval()
, it is mostly evil. You cannot use it safely on user input, as shown in "Eval really is dangerous". Imagine an adversary who is calling your API and instead of using your form or such just sends you something along the lines of
body=byebye&table=__import__("os").system("rm -rf /")
Depending on your setup you might lose your entire system, or the container, or what have you. And that is not the only thing they could do. They have the whole of Python expressions to play with.
In your case a proper way to handle user selected tables (models) is to have a lookup:
the_5_tables = {
"table_1": Table1,
"table_2": Table2,
# etc.
}
Then all you need to do is
#table to be searched
model = the_5_tables[request.params['table']]
This has the added benefit of whitelisting what tables the user is able to use, even if the current scope has access to others.
Producing the filters is easier done using the actual Column
objects instead of their keys:
results = {}
for col in model.__table__.columns:
try:
searchdb = request.dbsession.query(model).filter(col == search).all()
results[col.key] = searchdb
except Exception as e:
print(f"Unhandled error: {e}")
continue
return results
This will unfortunately make as many roundtrips to the database as you have columns in your table. Now, I'm guessing that the bare except:
was there in order to mask errors arising from type mismatches, for example when you're trying to search against a numeric column. You could inspect the columns further to avoid that:
from sqlalchemy.types import String
# ...
results = {}
for col in model.__table__.columns:
if isinstance(col.type, String):
searchdb = request.dbsession.query(model).filter(col == search).all()
results[col.key] = searchdb
return results
Taking it even further, if you're not actually that interested in which columns matched, you could just form a single query such as:
from sqlalchemy import literal
from sqlalchemy.types import String
# ...
str_cols = [c for c in model.__table__.c if isinstance(c.type, String)]
results = request.dbsession.query(model).filter(literal(search).in_(str_cols)).all()
return results
Though a bit hacky, it's still possible to also fetch which column(s) matched in a single query (this is not that useful; it's trivial to do the same in Python after the query):
from sqlalchemy import func
# ...
results = request.dbsession.query(
model,
func.concat_ws(",", *[
func.if_(c == search, c.key, None)
for c in str_cols
]).label("in_columns")).\
filter(literal(search).in_(str_cols)).all()
return results
The in_columns
will be a comma separated string of column names that matched.