Repository: Kinto/kinto

Issue:
Crash postgresql backend: TypeError: argument of type 'int' is not iterable
Apparently happens with `?like_target.version=65`

This works: https://buildhub.prod.mozaws.net/v1/buckets/build-hub/collections/releases/records?_limit=1&like_target.version=%2265%22

This crashes: https://buildhub.prod.mozaws.net/v1/buckets/build-hub/collections/releases/records?_limit=1&like_target.version=65

```
TypeError: argument of type 'int' is not iterable
(12 additional frame(s) were not displayed)
...
  File "kinto/core/resource/__init__.py", line 292, in collection_get
    include_deleted=include_deleted)
  File "kinto/core/resource/model.py", line 100, in get_records
    auth=self.auth)
  File "statsd/client.py", line 37, in _wrapped
    return_value = f(*args, **kwargs)
  File "kinto/core/storage/postgresql/__init__.py", line 634, in get_all
    modified_field)
  File "kinto/core/storage/postgresql/__init__.py", line 741, in _format_conditions
    if '*' not in value
```

A like query with a value that is not a string should return 400

Like it is done here:
https://github.com/Kinto/kinto/blob/ea5f66dd4441447292ce26a068bdb690b7de7496/kinto/core/resource/__init__.py#L1004-L1011

https://sentry.prod.mozaws.net/operations/buildhub-prod/issues/4990222/


Discussion:

Resolving patch:
diff --git a/kinto/core/resource/__init__.py b/kinto/core/resource/__init__.py
--- a/kinto/core/resource/__init__.py
+++ b/kinto/core/resource/__init__.py
@@ -1001,6 +1001,11 @@ def _extract_filters(self):
                 error_details["description"] = error_msg
                 raise_invalid(self.request, **error_details)
 
+            # Return 400 if _limit is not a string
+            if operator == COMPARISON.LIKE:
+                if not isinstance(value, str):
+                    raise_invalid(self.request, **error_details)
+
             if operator in (COMPARISON.IN, COMPARISON.EXCLUDE):
                 all_integers = all([isinstance(v, int) for v in value])
                 all_strings = all([isinstance(v, str) for v in value])


Test patch:
diff --git a/tests/core/resource/test_filter.py b/tests/core/resource/test_filter.py
--- a/tests/core/resource/test_filter.py
+++ b/tests/core/resource/test_filter.py
@@ -123,6 +123,29 @@ def test_string_filters_naively_by_value(self):
         result = self.resource.collection_get()
         self.assertEqual(len(result["data"]), 6)
 
+    def test_not_string_filter(self):
+        self.validated["querystring"] = {"like_title": 10}
+        try:
+            self.resource.collection_get()
+        except httpexceptions.HTTPBadRequest as e:
+            error = e
+        self.assertEqual(
+            error.json,
+            {
+                "code": 400,
+                "details": [
+                    {
+                        "description": "Invalid value for like_title",
+                        "location": "querystring",
+                        "name": "like_title",
+                    }
+                ],
+                "errno": 107,
+                "error": "Invalid parameters",
+                "message": "Invalid value for like_title",
+            },
+        )
+
     def test_string_filters_searching_by_value_not_matching(self):
         self.validated["querystring"] = {"like_title": "MoFoo"}
         result = self.resource.collection_get()