escaping query for SQL LIKE in rails - sql

In rails source (https://github.com/rails/rails/blob/fe4b0eee05f59831e1468ed50f55fbad0ce11e1d/activerecord/lib/active_record/sanitization.rb#L112) there is a sanitize_sql_like method that (I am hoping) will sanitize strings before using them with a SQL LIKE
however, I can't seem to use that, as Rails says that method doesn't exist.
My string has an apostrophe in it and the query is
#query = "Joe's"
Model.where("lower(field) LIKE ?", "%#{#query}%")
Using ActiveRecord::Base.sanitize doesn't help, as there are no results for the query.
How can I escape #query, and keep my SQL secured?

I've solved this same problem (on MySQL) using ActiveRecord::Sanitization::ClassMethods to properly sanitize user input. Unfortunately, the methods defined in ActiveRecord::Sanitization::ClassMethods are declared as protected, so they are only accessible in the scope of a Model class. The methods defined in ActiveRecord::Sanitization::ClassMethods are mixed in to all Model classes, so they are available directly from within the scope of a Model. That requires you to define a class/instance method or scope (ActiveRecord scope) on your model to use them, rather than using them externally, as in your example. However, design-wise, it's probably preferable to encapsulate the query logic in the Model anyway.
This solution also has the advantage of not only escaping the single-quote character, but also escaping other characters that would be interpreted by a SQL like query, such as the '%' character (among others). This should properly prevent SQL injection by escaping the the other characters which might be able to cause values to be interpreted rather than being treated as literals. There are also additional sanitization methods defined in ActiveRecord::Sanitization::ClassMethods that are useful for embedding user input (or other tainted input) in other SQL query contexts. Here is a working solution, tested on MySQL.
class Model < ActiveRecord::Base
# Finds a Model by case-insensitive substring match on Model.field
#
# #param query [String] A value to use in the substring match.
# #return [ActiveRecord::Relation] An `Relation` of `Model`s whose
# `field` includes the `query` substring.
scope :find_by_field_substring, ->(query) do
where(arel_table[:field].matches("%#{sanitize_sql_like(query)}%"))
end
end
You can then access this scope like this:
Model.find_by_field_substring "Joe's"
=> #<ActiveRecord::Relation [#<Model id: 1, field: "Joe's">]>
The usage of both ActiveRecord scopes and ActiveRecord::Relations are pretty well documented, but may only be available in newer versions of Rails.
Note that your database may require a second parameter to the sanitize_sql_like method to specify a different escape character than '\'.
Also note that escaping like this doesn't work for MySQL if it is running in NO_BACKSLASH_ESCAPES mode, because it forces a different mechanism for escaping values. If you're using NO_BACKSLASH_ESCAPES mode see this answer.
I haven't tested this part, but rather than using Arel, the SQL AST builder that sits underneath ActiveRecord, one could probably also use the style suggested in your initial example, as long as it is defined within the Model class.
class Model < ActiveRecord::Base
scope :find_by_field_substring, ->(query) do
where("lower(field) LIKE ?", "%#{sanitize_sql_like(query)}%")
end
end

If you use where properly, it will escape the input automatically
#query = "Joe's"
Model.where("lower(field) LIKE ?", "%#{#query}%")
Just note that your query is wrong. You have a lower() operator, then you pass an input which is not lower-case. The query will always return 0.
Moreover, lower() will reduce the ability of the database to use the index. In most databases, LIKE is already case insensitive (except for PostgreSQL where you should use ILIKE).
query = "Joe's"
Model.where("field LIKE ?", "%#{query}%")
or
query = "Joe's"
Model.where("field ILIKE ?", "%#{query}%")
Here's a real example on a real database. As you can see, the input is properly escaped in the final SQL.
> query = "Joe's"
> User.where("lower(email) LIKE ?", "%#{query}%")
User Load (4.4ms) SELECT "users".* FROM "users" WHERE (lower(email) LIKE '%Joe''s%')
=> #<ActiveRecord::Relation []>

Short and illegal answer:
ActiveRecord::Base.send(:sanitize_sql_like, text_to_escape)
Here's the output in the rails console (postgres)
irb(main):001:0> ActiveRecord::Base.send(:sanitize_sql_like, '%foo_bar%')
=> "\\%foo\\_bar\\%"
sanitize_sql_like is not a public method, so you should use it wisely.
You are not using this method just because of the apostrophe, it also escapes characters like %_ that are wildcards in Postgres.
This answer could be useful in case you want to extend ActiveRecord::Base, so all your models can include this method:
Rails extending ActiveRecord::Base

As previously stated, you can still use the sanitization methods if you use them within context of the model. Although adding a scope works (as shown in a previous answer), it's not strictly necessary. For example, you can add this to your model:
def self.where_ilike(search_terms)
where('search_tokens ILIKE ?', "%#{sanitize_sql_like(search_terms)}%")
end

Related

PL/SQL mysterious arrow operator

What is the operator "=>" we can often see in the UTL_TCP usage examples ?
As in http://www.oracle-base.com/articles/misc/ftp-from-plsql.php
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.ascii(p_conn => l_conn);
ftp.get(p_conn => l_conn,
p_from_file => '/u01/app/oracle/test.txt',
p_to_dir => 'MY_DOCS',
p_to_file => 'test_get.txt');
ftp.logout(l_conn);
I dont understand what is the purpore of "p_conn => l_conn", since we never use p_conn anywhere.
Even closing the connection is done with ftp.logout(l_conn), not using p_conn.
All the variables used before this operator "=>" arent even defined anywhere.
Maybe it's an operator specific to the UTL_TCP package, because I never saw it being used anywhere else, and cant find it in any PL/SQL documentation, Oracle or otherwise.
It's a method to pass parameters to a PL/SQL subroutine called named notation.
For more information see the official Oracle documentation:
http://docs.oracle.com/cd/B12037_01/appdev.101/b10807/08_subs.htm#sthref1013
It's mostly used when you don't know how much parameters a procedure does expect or in which order they are expected. So you just name every parameter you want to pass with it's according value.
Excerpt from the documentation:
Positional notation. You specify the same parameters in the same order as they are declared in the procedure.
This notation is compact, but if you specify the parameters (especially literals) in the wrong order, the bug can be hard to detect. You must change your code if the procedure's parameter list changes.
Named notation. You specify the name of each parameter along with its value. An arrow (=>) serves as the association operator. The order of the parameters is not significant.
This notation is more verbose, but makes your code easier to read and maintain. You can sometimes avoid changing your code if the procedure's parameter list changes, for example if the parameters are reordered or a new optional parameter is added. Named notation is a good practice to use for any code that calls someone else's API, or defines an API for someone else to use.
Mixed notation. You specify the first parameters with positional notation, then switch to named notation for the last parameters.
You can use this notation to call procedures that have some required parameters, followed by some optional parameters.
Excerpt from documentation:
Positional Versus Named Notation for Subprogram Parameters
When calling a subprogram, you can write the actual parameters using either positional or named notation. That is, you can indicate the association between an actual and formal parameter by position or name. So, given the declarations
DECLARE
acct INTEGER;
amt REAL;
PROCEDURE credit_acct (acct_no INTEGER, amount REAL) IS ...
you can call the procedure credit_acct in four logically equivalent ways:
BEGIN
credit_acct(acct, amt); -- positional notation
credit_acct(amount => amt, acct_no => acct); -- named notation
credit_acct(acct_no => acct, amount => amt); -- named notation
credit_acct(acct, amount => amt); -- mixed notation

Pass Java List to SQL query Grails

i have a populated list:
def someList=... (string values)
and I want to pass this into a SQL statement to restrict which columns the query selects.
db.rows("select ${someList} from arch_application")
However, I get this error when I try to do so:
There is a ? parameter in the select list. This is not allowed.
Anyone have an ideas? Thanks!
When you pass a GString to Sql.rows, it gets parsed differently than normal in groovy. In particular, it creates a PreparedStatement with replaceable parameters for ${} substitutions. In your case this is probably not what you want. Try forcing the GString to a Java string:
db.rows("select ${someList.join(',')} from arch_application" as String)

Rails3 activerecord hashset customization

I want to pass hashset to ActiveRecord finder method Model_name.where({ :key => value }). This works perfectly, but SQL composed from that uses straight comparison =. Is it possible to customize this and switch to LIKE comparison usage with hashset?
The :key => value syntax only works for =, IN, and BETWEEN conditions (depending on whether value is atomic, an Array, or a Range). Anything else requires you to pass the SQL as a string:
Model.where("key LIKE ?", value)

Rails - escaping SQL params

I am doing some plain SQLs in my rails model (for purists this is just for complex SQLs :)
Since I am not using find*/condition methods, is there a helper method that I can use straight to do that?
The quote method on the connection object escapes strings. When building up queries, use sanitize_sql_for_conditions to convert ActiveRecord conditions hashes or arrays to SQL WHERE clauses.
The methods in ActiveRecord::ConnectionAdapters::DatabaseStatements are handy for direct queries, in particular the ones starting with select_.
Rails uses sanitize_sql_for_conditions internally for dealing with placeholders. Of course, that method is protected so you can't (cleanly) use it outside of an ActiveRecord model. You can get around the protectedness using send:
escaped_string = Model.send(:sanitize_sql_for_conditions, [
'id = ? and name = ?',
params[:id], params[:name]
]
)

Lambdas with captured variables

Consider the following line of code:
private void DoThis() {
int i = 5;
var repo = new ReportsRepository<RptCriteriaHint>();
// This does NOT work
var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();
// This DOES work
var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();
}
So when I hardwire an actual number into the lambda function, it works fine. When I use a captured variable into the expression it comes back with the following error:
No mapping exists from object type
ReportBuilder.Reporter+<>c__DisplayClass0
to a known managed provider native
type.
Why? How can I fix it?
Technically, the correct way to fix this is for the framework that is accepting the expression tree from your lambda to evaluate the i reference; in other words, it's a LINQ framework limitation for some specific framework. What it is currently trying to do is interpret the i as a member access on some type known to it (the provider) from the database. Because of the way lambda variable capture works, the i local variable is actually a field on a hidden class, the one with the funny name, that the provider doesn't recognize.
So, it's a framework problem.
If you really must get by, you could construct the expression manually, like this:
ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
Expression.Lambda<Func<RptCriteriaHint,bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
x,
typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
Expression.Constant(i)),
x)).ToList();
... but that's just masochism.
Your comment on this entry prompts me to explain further.
Lambdas are convertible into one of two types: a delegate with the correct signature, or an Expression<TDelegate> of the correct signature. LINQ to external databases (as opposed to any kind of in-memory query) works using the second kind of conversion.
The compiler converts lambda expressions into expression trees, roughly speaking, by:
The syntax tree is parsed by the compiler - this happens for all code.
The syntax tree is rewritten after taking into account variable capture. Capturing variables is just like in a normal delegate or lambda - so display classes get created, and captured locals get moved into them (this is the same behaviour as variable capture in C# 2.0 anonymous delegates).
The new syntax tree is converted into a series of calls to the Expression class so that, at runtime, an object tree is created that faithfully represents the parsed text.
LINQ to external data sources is supposed to take this expression tree and interpret it for its semantic content, and interpret symbolic expressions inside the tree as either referring to things specific to its context (e.g. columns in the DB), or immediate values to convert. Usually, System.Reflection is used to look for framework-specific attributes to guide this conversion.
However, it looks like SubSonic is not properly treating symbolic references that it cannot find domain-specific correspondences for; rather than evaluating the symbolic references, it's just punting. Thus, it's a SubSonic problem.