Browse Source

Fix the validation of flavor_extraspecs v2 API

"create flavor_extraspecs" v2 API does not validate the data type
of a request body. If invalid parameter is passed, an internal error
happens. If many invalid requests come, a log file would be occupied
with traceback.

In addition, it does not validate the lengths of both key and value of
extra_specs. extra_specs are stored into the instance_type_extra_specs
table, and the key and value are defined as String(255).

This patch fixes the validation code from the viewpoint of data type
and key/value length.

Closes-Bug: #1264220

Change-Id: I195bd5d45a896e9b26dd81dab1e49c9f939b4805
tags/2014.2.b1
Ken'ichi Ohmichi 5 years ago
parent
commit
8010c8faf9

+ 18
- 4
nova/api/openstack/compute/contrib/flavorextraspecs.py View File

@@ -24,6 +24,7 @@ from nova.compute import flavors
24 24
 from nova import db
25 25
 from nova import exception
26 26
 from nova.openstack.common.gettextutils import _
27
+from nova import utils
27 28
 
28 29
 authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
29 30
 
@@ -55,12 +56,25 @@ class FlavorExtraSpecsController(object):
55 56
             expl = _('No Request Body')
56 57
             raise exc.HTTPBadRequest(explanation=expl)
57 58
 
58
-    def _check_key_names(self, keys):
59
+    def _check_extra_specs(self, specs):
60
+        if type(specs) is not dict:
61
+            msg = _('Bad extra_specs provided')
62
+            raise exc.HTTPBadRequest(explanation=msg)
63
+
59 64
         try:
60
-            flavors.validate_extra_spec_keys(keys)
65
+            flavors.validate_extra_spec_keys(specs.keys())
61 66
         except exception.InvalidInput as error:
62 67
             raise exc.HTTPBadRequest(explanation=error.format_message())
63 68
 
69
+        for key, value in specs.iteritems():
70
+            try:
71
+                utils.check_string_length(key, 'extra_specs key',
72
+                                          min_length=1, max_length=255)
73
+                utils.check_string_length(value, 'extra_specs value',
74
+                                          max_length=255)
75
+            except exception.InvalidInput as error:
76
+                raise exc.HTTPBadRequest(explanation=error.format_message())
77
+
64 78
     @wsgi.serializers(xml=ExtraSpecsTemplate)
65 79
     def index(self, req, flavor_id):
66 80
         """Returns the list of extra specs for a given flavor."""
@@ -74,7 +88,7 @@ class FlavorExtraSpecsController(object):
74 88
         authorize(context, action='create')
75 89
         self._check_body(body)
76 90
         specs = body.get('extra_specs')
77
-        self._check_key_names(specs.keys())
91
+        self._check_extra_specs(specs)
78 92
         try:
79 93
             db.flavor_extra_specs_update_or_create(context,
80 94
                                                               flavor_id,
@@ -87,7 +101,7 @@ class FlavorExtraSpecsController(object):
87 101
     def update(self, req, flavor_id, id, body):
88 102
         context = req.environ['nova.context']
89 103
         authorize(context, action='update')
90
-        self._check_body(body)
104
+        self._check_extra_specs(body)
91 105
         if id not in body:
92 106
             expl = _('Request body and URI mismatch')
93 107
             raise exc.HTTPBadRequest(explanation=expl)

+ 44
- 11
nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py View File

@@ -142,7 +142,7 @@ class FlavorsExtraSpecsTest(test.TestCase):
142 142
         self.assertRaises(exception.NotAuthorized, self.controller.create,
143 143
                           req, 1, body)
144 144
 
145
-    def test_create_empty_body(self):
145
+    def _test_create_bad_request(self, body):
146 146
         self.stubs.Set(nova.db,
147 147
                        'flavor_extra_specs_update_or_create',
148 148
                        return_create_flavor_extra_specs)
@@ -150,7 +150,27 @@ class FlavorsExtraSpecsTest(test.TestCase):
150 150
         req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs',
151 151
                                       use_admin_context=True)
152 152
         self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
153
-                          req, 1, '')
153
+                          req, 1, body)
154
+
155
+    def test_create_empty_body(self):
156
+        self._test_create_bad_request('')
157
+
158
+    def test_create_non_dict_extra_specs(self):
159
+        self._test_create_bad_request({"extra_specs": "non_dict"})
160
+
161
+    def test_create_non_string_value(self):
162
+        self._test_create_bad_request({"extra_specs": {"key1": None}})
163
+
164
+    def test_create_zero_length_key(self):
165
+        self._test_create_bad_request({"extra_specs": {"": "value1"}})
166
+
167
+    def test_create_long_key(self):
168
+        key = "a" * 256
169
+        self._test_create_bad_request({"extra_specs": {key: "value1"}})
170
+
171
+    def test_create_long_value(self):
172
+        value = "a" * 256
173
+        self._test_create_bad_request({"extra_specs": {"key1": value}})
154 174
 
155 175
     @mock.patch('nova.db.flavor_extra_specs_update_or_create')
156 176
     def test_create_invalid_specs_key(self, mock_flavor_extra_specs):
@@ -199,7 +219,7 @@ class FlavorsExtraSpecsTest(test.TestCase):
199 219
         self.assertRaises(exception.NotAuthorized, self.controller.update,
200 220
                           req, 1, 'key1', body)
201 221
 
202
-    def test_update_item_empty_body(self):
222
+    def _test_update_item_bad_request(self, body):
203 223
         self.stubs.Set(nova.db,
204 224
                        'flavor_extra_specs_update_or_create',
205 225
                        return_create_flavor_extra_specs)
@@ -207,18 +227,31 @@ class FlavorsExtraSpecsTest(test.TestCase):
207 227
         req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs' +
208 228
                                       '/key1', use_admin_context=True)
209 229
         self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
210
-                          req, 1, 'key1', '')
230
+                          req, 1, 'key1', body)
231
+
232
+    def test_update_item_empty_body(self):
233
+        self._test_update_item_bad_request('')
211 234
 
212 235
     def test_update_item_too_many_keys(self):
213
-        self.stubs.Set(nova.db,
214
-                       'flavor_extra_specs_update_or_create',
215
-                       return_create_flavor_extra_specs)
216 236
         body = {"key1": "value1", "key2": "value2"}
237
+        self._test_update_item_bad_request(body)
217 238
 
218
-        req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs' +
219
-                                      '/key1', use_admin_context=True)
220
-        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
221
-                          req, 1, 'key1', body)
239
+    def test_update_item_non_dict_extra_specs(self):
240
+        self._test_update_item_bad_request("non_dict")
241
+
242
+    def test_update_item_non_string_value(self):
243
+        self._test_update_item_bad_request({"key1": None})
244
+
245
+    def test_update_item_zero_length_key(self):
246
+        self._test_update_item_bad_request({"": "value1"})
247
+
248
+    def test_update_item_long_key(self):
249
+        key = "a" * 256
250
+        self._test_update_item_bad_request({key: "value1"})
251
+
252
+    def test_update_item_long_value(self):
253
+        value = "a" * 256
254
+        self._test_update_item_bad_request({"key1": value})
222 255
 
223 256
     def test_update_item_body_uri_mismatch(self):
224 257
         self.stubs.Set(nova.db,

Loading…
Cancel
Save