In some cases, invalid input to exposed elements of the view leads to broken SQL, and an ugly error message. This is also _falsely_ triggered by some security scanners as a SQL injection vulnerability.

The specific case I've seen is for a page view with an exposed operator, when an illegal value is passed in the operator parameter. Here's the code which I think is the root of the issue (in view.inc, function build($display_id = NULL)):


if ($this->display_handler->uses_exposed()) {
$this->exposed_widgets = $this->render_exposed_form();
if (form_set_error() || !empty($this->build_info['abort'])) {
$this->built = TRUE;
return empty($this->build_info['fail']);
}
}

When form_set_error() to return errors, but build_info['abort'] and build_info['fail'] are empty, the return value from the build() function is TRUE but the actual query is never built. You end up with a message like this:

# An illegal choice has been detected. Please contact the site administrator.
# user warning: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN node_access na ON na.nid = node.nid WHERE (na.grant_view >= 1 AND ((n' at line 1 query: SELECT COUNT(*) FROM ( INNER JOIN node_access na ON na.nid = node.nid WHERE (na.grant_view >= 1 AND ((na.gid = 0 AND na.realm = 'all') OR (na.gid = 0 AND na.realm = 'og_public') OR (na.gid = 1 AND na.realm = 'term_access')))) count_alias in .../sites/all/modules/views/includes/view.inc on line 760.

As you can see, it's trying to select count(*) from an broken subquery.

... SELECT COUNT(*) FROM ( INNER JOIN node_access ...

My first thought was to just put a patch in the set build_info['fail'] to TRUE whenever form_set_error() is true, but then I was afraid that this would break some cases where errors are correctly handled.

Here's what I've got now, but it's ugly as sin.

if ($this->display_handler->uses_exposed()) {
$this->exposed_widgets = $this->render_exposed_form();
if (form_set_error() || !empty($this->build_info['abort'])) {
if (form_set_error()) {
foreach (form_set_error() as $key => $value) {
if ($value == t('An illegal choice has been detected. Please contact the site administrator.')) {
$this->build_info['fail'] = TRUE;
}
}
}
$this->built = TRUE;
return empty($this->build_info['fail']);
}
}

Wold someone who knows the views module let me know what they think?

Also, I believe this is what comment #5 in http://drupal.org/node/370680 is about, and also what http://drupal.org/node/631640 is about, but I'm not confident enough to go monkeying around and marking things as duplicates.

Thanks!

CommentFileSizeAuthor
#10 view.inc-fixSQLErrors.patch670 bytesmstef

Comments

merlinofchaos’s picture

Hmm. The problem is that it's trying to run an empty query. It should not be running the empty query (and consequently not rewriting hte empty query to make it not empty). I could swear I fixed this already, but apparently not.

asacamano’s picture

Exactly - the reason it's trying to run the empty query is that the build() function is returning TRUE, so the execute() function thinks it's goo\d to go, but in fact the query is still empty because of the check for form_set_error(). I do tend to get verbose.

askit’s picture

We do need a fix. The "patch" provided by asacamano doesn't work always. For example, when we use exposed taxonomy term filter, It returns the following "Unable to find term: xxx" instead of "An illegal choice has been detected..."

The issue makes the exposed taxonomy term filter totally not usable. Could you give any hints and tips on the possible fixes? Thank you!

Anonymous’s picture

I am having a problem caused by the code you're discussing (lines 582 - 588 in view.inc), that might be of interest to anyone attempting the same (flawed?) method of generating views.

I am executing a view programmatically using an exposed filter, i.e.

...
$view->exposed_input['field_value'] = $filter_value;
$view->execute();
...

and this generates content for a block.

This block is then placed on a node add or edit page i.e. node/add, or node/%node/edit.

If the node form fails validation i.e. form_set_error() returns array of failed node fields and messages, then my programmatically generated view using exposed filters, stops being built and returns an empty $view->build_info['query'] string, due to line 584 in view.inc conditional statement if (form_set_error...

This then results in a SQL error for the view because the SELECT part of the query is missing:

user warning: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN node_access na ON na.nid = node.nid WHERE (na.grant_view >= 1 AND ((n' at line 1 query: INNER JOIN node_access na ON na.nid = node.nid WHERE...

Views code is assuming the form_set_error applies to the exposed filter form and stops building the view, but actually the form_set_error applies to the node form and not the exposed filter form.

I strongly suspect my choice of method is a bad one, and the solution is to find an alternative way of defining filters programmatically, perhaps using hook_views_query_alter to add the filter values?

sdelbosc’s picture

Just to track the issue...

Exploratus’s picture

I have a problem in my node add form -- when I write the words "select from" <--- in that order -- and try to save a node I get a the requested page is not found. Something about the words select from, in that order, breaks the links in the submit button on drupal. Seems like it thinks its an SQL command or something..

mstef’s picture

Priority: Normal » Major

I found the problem...

View.inc - Line 701 - execute()

$query = db_rewrite_sql($this->build_info['query'], $this->base_table, $this->base_field, array('view' => &$this));

SHOULD BE:

$query = $this->build_info['query'] ? db_rewrite_sql($this->build_info['query'], $this->base_table, $this->base_field, array('view' => &$this)) : NULL;

With invalid exposed filter input, a query is never generated. Inside execute(), on line 721, Views does if ($query), because if there is no query, no view should be executed. Running db_rewrite_sql() will generate a piece of a query, if you have a module that uses node_access, like OG. So, the View crashes with an SQL error. If we add this simple check to see if $this->build_info['query'] is empty, the $query will remain empty, and there will be no problem.

That only took 10 hours to figure out..

I hope we can get Views updated soon.

mstef’s picture

Version: 6.x-2.8 » 6.x-2.11
mstef’s picture

Status: Active » Needs review
mstef’s picture

StatusFileSize
new670 bytes

Patch

dawehner’s picture

Was this the taxonomy autocomplete form? There is a other issue with fixes this

mstef’s picture

Taxonomy, username, etc..

dawehner’s picture

Status: Needs review » Closed (duplicate)

See http://drupal.org/node/571234

I think this is technical definitive a duplicate

mstef’s picture

Regardless of if the issue was fixed, my patch addresses another portion of the code that should be taken care of.

You generate $query then ask if it exists - that doesn't make sense. $this->build_info['query'] should be checked for doing the db_rewrite.

Don't agree?

merlinofchaos’s picture

Actually if build_info['query'] does not exist, that means there are other problems which this patch masks. There are other ways to abort the build, and if there is no query, it shouldn't even get into that piece of code.

mstef’s picture

Actually if build_info['query'] does not exist, that means there are other problems

Then why is there a if($query) check later on?

Seems that if there is an error on the exposed form, Views should stop with the execution..?