The vulnerable parts of your query are: queryFieldNameFromUserInput, objectNameFromUserInput, whereFieldNameFromUserInput, and whereFieldValueFromUserInput.
Depending on your controller's "sharing" keyword, a comprehensive check would include something like the following:
String
queryFieldNameFromUserInput, objectNameFromUserInput, whereFieldNameFromUserInput, whereFieldValueFromUserInput;
Boolean isObjectAccessible = false, isFieldAccessible = String.isNotBlank(queryFieldNameFromUserInput),
isWhereAccessible;
SObjectType baseType;
DescribeSObjectResult baseDescribe;
Map<String, SObjectField> fields;
// Check object accessibility
try {
isObjectAccessible =
(baseDescribe =
(baseType = ((SObject)Type.forName(objectNameFromUserInput).newInstance())
.getSObjectType())
.getDescribe())
.isAccessible();
} catch(Exception e) {
}
if(!isObjectAccessible) {
return; // display an error or something here
}
fields = baseDescribe.fields.getMap();
// Check field accessibility
try {
for(String field: queryFieldNameFromUserInput.replaceAll('\\s+','').split(',')) {
isFieldAccessible &= fields.get(field).getDescribe().isAccessible();
}
} catch(Exception e) {
isFieldAccessible = false;
}
if(!isFieldAccessible) {
return; // display an error or something here
}
// Check more field accessibility
try {
isWhereAccessible = fields.get(whereFieldNameFromUserInput).getDescribe().isAccessible();
} catch(Exception e) {
isWhereAccessible = false;
}
if(!isWhereAccessible) {
return; // display an error or something here
}
// Do query
SObject[] results = Database.query(
'SELECT '+queryFieldNameFromUserInput+
' FROM '+objectNameFromUserInput+
' WHERE '+whereFieldNameFromUserInput+
' LIKE %'+String.escapeSingleQuotes(whereFieldValueFromUserInput)+'%'
);
Note that I still consider this incomplete, because you'd also need to check the data type for the whereFieldNameFromUserInput
, because you might need it to be a number, date, date/time, Boolean, etc, or the query itself can still crash.
And, as I mentioned in the second sentence of this answer, you also need to make sure that your class is declared as public with sharing class XYZ
, to make sure the query cannot return records the user cannot see.