We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implicit collection creation doesn't validate the ID format
```
$ http GET http://localhost:8888/v1/buckets/default/collections/見/records --auth natim:
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Content-Length, Expires, Alert, Retry-After, Last-Modified, Total-Records, ETag, Pragma, Cache-Control, Backoff, Next-Page
Cache-Control: no-cache
Content-Length: 11
Content-Type: application/json; charset=UTF-8
Date: Thu, 05 Nov 2015 14:19:38 GMT
Etag: "1446733178498"
Last-Modified: Thu, 05 Nov 2015 14:19:38 GMT
Server: waitress
Total-Records: 0

{
    "data": []
}
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kinto/views/buckets.py`
Content:
```
1 from pyramid import httpexceptions
2 from pyramid.settings import asbool
3 from pyramid.security import NO_PERMISSION_REQUIRED
4 from pyramid.view import view_config
5 
6 from cliquet import resource
7 from cliquet.utils import build_request, reapply_cors
8 from cliquet.storage import exceptions as storage_exceptions
9 
10 from kinto.authorization import RouteFactory
11 from kinto.views import NameGenerator
12 from kinto.views.collections import Collection
13 
14 
15 @resource.register(name='bucket',
16                    collection_methods=('GET', 'POST'),
17                    collection_path='/buckets',
18                    record_path='/buckets/{{id}}')
19 class Bucket(resource.ProtectedResource):
20     permissions = ('read', 'write', 'collection:create', 'group:create')
21 
22     def __init__(self, *args, **kwargs):
23         super(Bucket, self).__init__(*args, **kwargs)
24         self.model.id_generator = NameGenerator()
25 
26     def get_parent_id(self, request):
27         # Buckets are not isolated by user, unlike Cliquet resources.
28         return ''
29 
30     def delete(self):
31         result = super(Bucket, self).delete()
32 
33         # Delete groups.
34         storage = self.model.storage
35         parent_id = '/buckets/%s' % self.record_id
36         storage.delete_all(collection_id='group',
37                            parent_id=parent_id,
38                            with_deleted=False)
39         storage.purge_deleted(collection_id='group',
40                               parent_id=parent_id)
41 
42         # Delete collections.
43         deleted = storage.delete_all(collection_id='collection',
44                                      parent_id=parent_id,
45                                      with_deleted=False)
46         storage.purge_deleted(collection_id='collection',
47                               parent_id=parent_id)
48 
49         # Delete records.
50         id_field = self.model.id_field
51         for collection in deleted:
52             parent_id = '/buckets/%s/collections/%s' % (self.record_id,
53                                                         collection[id_field])
54             storage.delete_all(collection_id='record',
55                                parent_id=parent_id,
56                                with_deleted=False)
57             storage.purge_deleted(collection_id='record', parent_id=parent_id)
58 
59         return result
60 
61 
62 def create_bucket(request, bucket_id):
63     """Create a bucket if it doesn't exists."""
64     bucket_put = (request.method.lower() == 'put' and
65                   request.path.endswith('buckets/default'))
66     # Do nothing if current request will already create the bucket.
67     if bucket_put:
68         return
69 
70     # Do not intent to create multiple times per request (e.g. in batch).
71     already_created = request.bound_data.setdefault('buckets', {})
72     if bucket_id in already_created:
73         return
74 
75     # Fake context to instantiate a Bucket resource.
76     context = RouteFactory(request)
77     context.get_permission_object_id = lambda r, i: '/buckets/%s' % bucket_id
78     resource = Bucket(request, context)
79     try:
80         bucket = resource.model.create_record({'id': bucket_id})
81     except storage_exceptions.UnicityError as e:
82         bucket = e.record
83     already_created[bucket_id] = bucket
84 
85 
86 def create_collection(request, bucket_id):
87     # Do nothing if current request does not involve a collection.
88     subpath = request.matchdict.get('subpath')
89     if not (subpath and subpath.startswith('collections/')):
90         return
91 
92     collection_id = subpath.split('/')[1]
93     collection_uri = '/buckets/%s/collections/%s' % (bucket_id, collection_id)
94 
95     # Do not intent to create multiple times per request (e.g. in batch).
96     already_created = request.bound_data.setdefault('collections', {})
97     if collection_uri in already_created:
98         return
99 
100     # Do nothing if current request will already create the collection.
101     collection_put = (request.method.lower() == 'put' and
102                       request.path.endswith(collection_id))
103     if collection_put:
104         return
105 
106     # Fake context to instantiate a Collection resource.
107     context = RouteFactory(request)
108     context.get_permission_object_id = lambda r, i: collection_uri
109 
110     backup = request.matchdict
111     request.matchdict = dict(bucket_id=bucket_id,
112                              id=collection_id,
113                              **request.matchdict)
114     resource = Collection(request, context)
115     try:
116         collection = resource.model.create_record({'id': collection_id})
117     except storage_exceptions.UnicityError as e:
118         collection = e.record
119     already_created[collection_uri] = collection
120     request.matchdict = backup
121 
122 
123 @view_config(route_name='default_bucket', permission=NO_PERMISSION_REQUIRED)
124 @view_config(route_name='default_bucket_collection',
125              permission=NO_PERMISSION_REQUIRED)
126 def default_bucket(request):
127     if request.method.lower() == 'options':
128         path = request.path.replace('default', 'unknown')
129         subrequest = build_request(request, {
130             'method': 'OPTIONS',
131             'path': path
132         })
133         return request.invoke_subrequest(subrequest)
134 
135     if getattr(request, 'prefixed_userid', None) is None:
136         # Pass through the forbidden_view_config
137         raise httpexceptions.HTTPForbidden()
138 
139     settings = request.registry.settings
140 
141     if asbool(settings['readonly']):
142         raise httpexceptions.HTTPMethodNotAllowed()
143 
144     bucket_id = request.default_bucket_id
145     path = request.path.replace('/buckets/default', '/buckets/%s' % bucket_id)
146     querystring = request.url[(request.url.index(request.path) +
147                                len(request.path)):]
148 
149     # Make sure bucket exists
150     create_bucket(request, bucket_id)
151 
152     # Make sure the collection exists
153     create_collection(request, bucket_id)
154 
155     subrequest = build_request(request, {
156         'method': request.method,
157         'path': path + querystring,
158         'body': request.body
159     })
160     subrequest.bound_data = request.bound_data
161 
162     try:
163         response = request.invoke_subrequest(subrequest)
164     except httpexceptions.HTTPException as error:
165         if error.content_type == 'application/json':
166             response = reapply_cors(subrequest, error)
167         else:
168             # Ask the upper level to format the error.
169             raise error
170     return response
171
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kinto/views/buckets.py b/kinto/views/buckets.py
--- a/kinto/views/buckets.py
+++ b/kinto/views/buckets.py
@@ -4,6 +4,7 @@
 from pyramid.view import view_config
 
 from cliquet import resource
+from cliquet.errors import raise_invalid
 from cliquet.utils import build_request, reapply_cors
 from cliquet.storage import exceptions as storage_exceptions
 
@@ -112,6 +113,12 @@
                              id=collection_id,
                              **request.matchdict)
     resource = Collection(request, context)
+    if not resource.model.id_generator.match(collection_id):
+        error_details = {
+            'location': 'path',
+            'description': "Invalid collection_id id"
+        }
+        raise_invalid(request, **error_details)
     try:
         collection = resource.model.create_record({'id': collection_id})
     except storage_exceptions.UnicityError as e:

{"golden_diff": "diff --git a/kinto/views/buckets.py b/kinto/views/buckets.py\n--- a/kinto/views/buckets.py\n+++ b/kinto/views/buckets.py\n@@ -4,6 +4,7 @@\n from pyramid.view import view_config\n \n from cliquet import resource\n+from cliquet.errors import raise_invalid\n from cliquet.utils import build_request, reapply_cors\n from cliquet.storage import exceptions as storage_exceptions\n \n@@ -112,6 +113,12 @@\n                              id=collection_id,\n                              **request.matchdict)\n     resource = Collection(request, context)\n+    if not resource.model.id_generator.match(collection_id):\n+        error_details = {\n+            'location': 'path',\n+            'description': \"Invalid collection_id id\"\n+        }\n+        raise_invalid(request, **error_details)\n     try:\n         collection = resource.model.create_record({'id': collection_id})\n     except storage_exceptions.UnicityError as e:\n", "issue": "Implicit collection creation doesn't validate the ID format\n```\n$ http GET http://localhost:8888/v1/buckets/default/collections/\u898b/records --auth natim:\nHTTP/1.1 200 OK\nAccess-Control-Expose-Headers: Content-Length, Expires, Alert, Retry-After, Last-Modified, Total-Records, ETag, Pragma, Cache-Control, Backoff, Next-Page\nCache-Control: no-cache\nContent-Length: 11\nContent-Type: application/json; charset=UTF-8\nDate: Thu, 05 Nov 2015 14:19:38 GMT\nEtag: \"1446733178498\"\nLast-Modified: Thu, 05 Nov 2015 14:19:38 GMT\nServer: waitress\nTotal-Records: 0\n\n{\n    \"data\": []\n}\n```\n\n", "before_files": [{"content": "from pyramid import httpexceptions\nfrom pyramid.settings import asbool\nfrom pyramid.security import NO_PERMISSION_REQUIRED\nfrom pyramid.view import view_config\n\nfrom cliquet import resource\nfrom cliquet.utils import build_request, reapply_cors\nfrom cliquet.storage import exceptions as storage_exceptions\n\nfrom kinto.authorization import RouteFactory\nfrom kinto.views import NameGenerator\nfrom kinto.views.collections import Collection\n\n\n@resource.register(name='bucket',\n                   collection_methods=('GET', 'POST'),\n                   collection_path='/buckets',\n                   record_path='/buckets/{{id}}')\nclass Bucket(resource.ProtectedResource):\n    permissions = ('read', 'write', 'collection:create', 'group:create')\n\n    def __init__(self, *args, **kwargs):\n        super(Bucket, self).__init__(*args, **kwargs)\n        self.model.id_generator = NameGenerator()\n\n    def get_parent_id(self, request):\n        # Buckets are not isolated by user, unlike Cliquet resources.\n        return ''\n\n    def delete(self):\n        result = super(Bucket, self).delete()\n\n        # Delete groups.\n        storage = self.model.storage\n        parent_id = '/buckets/%s' % self.record_id\n        storage.delete_all(collection_id='group',\n                           parent_id=parent_id,\n                           with_deleted=False)\n        storage.purge_deleted(collection_id='group',\n                              parent_id=parent_id)\n\n        # Delete collections.\n        deleted = storage.delete_all(collection_id='collection',\n                                     parent_id=parent_id,\n                                     with_deleted=False)\n        storage.purge_deleted(collection_id='collection',\n                              parent_id=parent_id)\n\n        # Delete records.\n        id_field = self.model.id_field\n        for collection in deleted:\n            parent_id = '/buckets/%s/collections/%s' % (self.record_id,\n                                                        collection[id_field])\n            storage.delete_all(collection_id='record',\n                               parent_id=parent_id,\n                               with_deleted=False)\n            storage.purge_deleted(collection_id='record', parent_id=parent_id)\n\n        return result\n\n\ndef create_bucket(request, bucket_id):\n    \"\"\"Create a bucket if it doesn't exists.\"\"\"\n    bucket_put = (request.method.lower() == 'put' and\n                  request.path.endswith('buckets/default'))\n    # Do nothing if current request will already create the bucket.\n    if bucket_put:\n        return\n\n    # Do not intent to create multiple times per request (e.g. in batch).\n    already_created = request.bound_data.setdefault('buckets', {})\n    if bucket_id in already_created:\n        return\n\n    # Fake context to instantiate a Bucket resource.\n    context = RouteFactory(request)\n    context.get_permission_object_id = lambda r, i: '/buckets/%s' % bucket_id\n    resource = Bucket(request, context)\n    try:\n        bucket = resource.model.create_record({'id': bucket_id})\n    except storage_exceptions.UnicityError as e:\n        bucket = e.record\n    already_created[bucket_id] = bucket\n\n\ndef create_collection(request, bucket_id):\n    # Do nothing if current request does not involve a collection.\n    subpath = request.matchdict.get('subpath')\n    if not (subpath and subpath.startswith('collections/')):\n        return\n\n    collection_id = subpath.split('/')[1]\n    collection_uri = '/buckets/%s/collections/%s' % (bucket_id, collection_id)\n\n    # Do not intent to create multiple times per request (e.g. in batch).\n    already_created = request.bound_data.setdefault('collections', {})\n    if collection_uri in already_created:\n        return\n\n    # Do nothing if current request will already create the collection.\n    collection_put = (request.method.lower() == 'put' and\n                      request.path.endswith(collection_id))\n    if collection_put:\n        return\n\n    # Fake context to instantiate a Collection resource.\n    context = RouteFactory(request)\n    context.get_permission_object_id = lambda r, i: collection_uri\n\n    backup = request.matchdict\n    request.matchdict = dict(bucket_id=bucket_id,\n                             id=collection_id,\n                             **request.matchdict)\n    resource = Collection(request, context)\n    try:\n        collection = resource.model.create_record({'id': collection_id})\n    except storage_exceptions.UnicityError as e:\n        collection = e.record\n    already_created[collection_uri] = collection\n    request.matchdict = backup\n\n\n@view_config(route_name='default_bucket', permission=NO_PERMISSION_REQUIRED)\n@view_config(route_name='default_bucket_collection',\n             permission=NO_PERMISSION_REQUIRED)\ndef default_bucket(request):\n    if request.method.lower() == 'options':\n        path = request.path.replace('default', 'unknown')\n        subrequest = build_request(request, {\n            'method': 'OPTIONS',\n            'path': path\n        })\n        return request.invoke_subrequest(subrequest)\n\n    if getattr(request, 'prefixed_userid', None) is None:\n        # Pass through the forbidden_view_config\n        raise httpexceptions.HTTPForbidden()\n\n    settings = request.registry.settings\n\n    if asbool(settings['readonly']):\n        raise httpexceptions.HTTPMethodNotAllowed()\n\n    bucket_id = request.default_bucket_id\n    path = request.path.replace('/buckets/default', '/buckets/%s' % bucket_id)\n    querystring = request.url[(request.url.index(request.path) +\n                               len(request.path)):]\n\n    # Make sure bucket exists\n    create_bucket(request, bucket_id)\n\n    # Make sure the collection exists\n    create_collection(request, bucket_id)\n\n    subrequest = build_request(request, {\n        'method': request.method,\n        'path': path + querystring,\n        'body': request.body\n    })\n    subrequest.bound_data = request.bound_data\n\n    try:\n        response = request.invoke_subrequest(subrequest)\n    except httpexceptions.HTTPException as error:\n        if error.content_type == 'application/json':\n            response = reapply_cors(subrequest, error)\n        else:\n            # Ask the upper level to format the error.\n            raise error\n    return response\n", "path": "kinto/views/buckets.py"}], "after_files": [{"content": "from pyramid import httpexceptions\nfrom pyramid.settings import asbool\nfrom pyramid.security import NO_PERMISSION_REQUIRED\nfrom pyramid.view import view_config\n\nfrom cliquet import resource\nfrom cliquet.errors import raise_invalid\nfrom cliquet.utils import build_request, reapply_cors\nfrom cliquet.storage import exceptions as storage_exceptions\n\nfrom kinto.authorization import RouteFactory\nfrom kinto.views import NameGenerator\nfrom kinto.views.collections import Collection\n\n\n@resource.register(name='bucket',\n                   collection_methods=('GET', 'POST'),\n                   collection_path='/buckets',\n                   record_path='/buckets/{{id}}')\nclass Bucket(resource.ProtectedResource):\n    permissions = ('read', 'write', 'collection:create', 'group:create')\n\n    def __init__(self, *args, **kwargs):\n        super(Bucket, self).__init__(*args, **kwargs)\n        self.model.id_generator = NameGenerator()\n\n    def get_parent_id(self, request):\n        # Buckets are not isolated by user, unlike Cliquet resources.\n        return ''\n\n    def delete(self):\n        result = super(Bucket, self).delete()\n\n        # Delete groups.\n        storage = self.model.storage\n        parent_id = '/buckets/%s' % self.record_id\n        storage.delete_all(collection_id='group',\n                           parent_id=parent_id,\n                           with_deleted=False)\n        storage.purge_deleted(collection_id='group',\n                              parent_id=parent_id)\n\n        # Delete collections.\n        deleted = storage.delete_all(collection_id='collection',\n                                     parent_id=parent_id,\n                                     with_deleted=False)\n        storage.purge_deleted(collection_id='collection',\n                              parent_id=parent_id)\n\n        # Delete records.\n        id_field = self.model.id_field\n        for collection in deleted:\n            parent_id = '/buckets/%s/collections/%s' % (self.record_id,\n                                                        collection[id_field])\n            storage.delete_all(collection_id='record',\n                               parent_id=parent_id,\n                               with_deleted=False)\n            storage.purge_deleted(collection_id='record', parent_id=parent_id)\n\n        return result\n\n\ndef create_bucket(request, bucket_id):\n    \"\"\"Create a bucket if it doesn't exists.\"\"\"\n    bucket_put = (request.method.lower() == 'put' and\n                  request.path.endswith('buckets/default'))\n    # Do nothing if current request will already create the bucket.\n    if bucket_put:\n        return\n\n    # Do not intent to create multiple times per request (e.g. in batch).\n    already_created = request.bound_data.setdefault('buckets', {})\n    if bucket_id in already_created:\n        return\n\n    # Fake context to instantiate a Bucket resource.\n    context = RouteFactory(request)\n    context.get_permission_object_id = lambda r, i: '/buckets/%s' % bucket_id\n    resource = Bucket(request, context)\n    try:\n        bucket = resource.model.create_record({'id': bucket_id})\n    except storage_exceptions.UnicityError as e:\n        bucket = e.record\n    already_created[bucket_id] = bucket\n\n\ndef create_collection(request, bucket_id):\n    # Do nothing if current request does not involve a collection.\n    subpath = request.matchdict.get('subpath')\n    if not (subpath and subpath.startswith('collections/')):\n        return\n\n    collection_id = subpath.split('/')[1]\n    collection_uri = '/buckets/%s/collections/%s' % (bucket_id, collection_id)\n\n    # Do not intent to create multiple times per request (e.g. in batch).\n    already_created = request.bound_data.setdefault('collections', {})\n    if collection_uri in already_created:\n        return\n\n    # Do nothing if current request will already create the collection.\n    collection_put = (request.method.lower() == 'put' and\n                      request.path.endswith(collection_id))\n    if collection_put:\n        return\n\n    # Fake context to instantiate a Collection resource.\n    context = RouteFactory(request)\n    context.get_permission_object_id = lambda r, i: collection_uri\n\n    backup = request.matchdict\n    request.matchdict = dict(bucket_id=bucket_id,\n                             id=collection_id,\n                             **request.matchdict)\n    resource = Collection(request, context)\n    if not resource.model.id_generator.match(collection_id):\n        error_details = {\n            'location': 'path',\n            'description': \"Invalid collection_id id\"\n        }\n        raise_invalid(request, **error_details)\n    try:\n        collection = resource.model.create_record({'id': collection_id})\n    except storage_exceptions.UnicityError as e:\n        collection = e.record\n    already_created[collection_uri] = collection\n    request.matchdict = backup\n\n\n@view_config(route_name='default_bucket', permission=NO_PERMISSION_REQUIRED)\n@view_config(route_name='default_bucket_collection',\n             permission=NO_PERMISSION_REQUIRED)\ndef default_bucket(request):\n    if request.method.lower() == 'options':\n        path = request.path.replace('default', 'unknown')\n        subrequest = build_request(request, {\n            'method': 'OPTIONS',\n            'path': path\n        })\n        return request.invoke_subrequest(subrequest)\n\n    if getattr(request, 'prefixed_userid', None) is None:\n        # Pass through the forbidden_view_config\n        raise httpexceptions.HTTPForbidden()\n\n    settings = request.registry.settings\n\n    if asbool(settings['readonly']):\n        raise httpexceptions.HTTPMethodNotAllowed()\n\n    bucket_id = request.default_bucket_id\n    path = request.path.replace('/buckets/default', '/buckets/%s' % bucket_id)\n    querystring = request.url[(request.url.index(request.path) +\n                               len(request.path)):]\n\n    # Make sure bucket exists\n    create_bucket(request, bucket_id)\n\n    # Make sure the collection exists\n    create_collection(request, bucket_id)\n\n    subrequest = build_request(request, {\n        'method': request.method,\n        'path': path + querystring,\n        'body': request.body\n    })\n    subrequest.bound_data = request.bound_data\n\n    try:\n        response = request.invoke_subrequest(subrequest)\n    except httpexceptions.HTTPException as error:\n        if error.content_type == 'application/json':\n            response = reapply_cors(subrequest, error)\n        else:\n            # Ask the upper level to format the error.\n            raise error\n    return response\n", "path": "kinto/views/buckets.py"}]}