To prevent SOQL injection in dynamic query builders while maintaining flexibility, follow these practices:
1. **Use Bind Variables**: Safely include user-supplied input in your queries using bind variables to ensure the input remains within its quoted context.
2. **Sanitize Input**: Use `String.escapeSingleQuotes()` to sanitize user-supplied strings, escaping single quotes to prevent breaking out of the quoted context.
3. **Whitelist Validation**: Validate user-supplied object or field names against a predefined whitelist of acceptable values. Use `Schema.getGlobalDescribe()` to retrieve valid names for comparison.
4. **Perform CRUD/FLS Checks**: Enforce CRUD (Create, Read, Update, Delete) and FLS (Field-Level Security) checks to ensure users have the necessary permissions for the specified objects and fields. Consider using modern approaches like `WITH USER_MODE` in SOQL queries or `AccessLevel.USER_MODE` for Database methods, which automatically enforce security permissions.
5. **Avoid User-Generated Queries in Apex**: Don't execute user-generated queries directly in Apex, as they bypass user permissions. Use the REST or SOAP API for safer execution if dynamic queries are necessary.
6. **Restrict Query Components**: Limit user-supplied input to specific query components like object names, field names, and WHERE clause inputs. Avoid allowing users to supply other parts of the query.
7. **Combine Methods for Complex Inputs**: For more complex scenarios, combine input sanitization with other security measures to address all potential risks.
These steps will help you build flexible yet secure dynamic queries in Salesforce.