From 9607e1aabd663c6d5d1b4b543fddbdc7a8d4a53d Mon Sep 17 00:00:00 2001 From: Ryan Brandt Date: Wed, 8 Jul 2015 13:57:22 -0600 Subject: [PATCH] Add check for restricted characters Remove placeholder test Adjust flake8 settings Add new mock requirements to test-requirements.txt Change-Id: I8f58341108710b1bc7431154cda096d6583d1ef3 --- monasca_api/tests/first_test.py | 21 ----- monasca_api/tests/test_validation.py | 86 +++++++++++++++++++ monasca_api/v2/common/validation.py | 36 ++++++++ monasca_api/v2/reference/alarm_definitions.py | 8 +- monasca_api/v2/reference/metrics.py | 16 ++-- test-requirements.txt | 1 + tox.ini | 9 ++ 7 files changed, 145 insertions(+), 32 deletions(-) delete mode 100644 monasca_api/tests/first_test.py create mode 100644 monasca_api/tests/test_validation.py create mode 100644 monasca_api/v2/common/validation.py diff --git a/monasca_api/tests/first_test.py b/monasca_api/tests/first_test.py deleted file mode 100644 index bb3891fc9..000000000 --- a/monasca_api/tests/first_test.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2014 Hewlett-Packard -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import unittest - - -class Test_first(unittest.TestCase): - - def test_first(self): - assert 1 == 1 diff --git a/monasca_api/tests/test_validation.py b/monasca_api/tests/test_validation.py new file mode 100644 index 000000000..3b1e89fee --- /dev/null +++ b/monasca_api/tests/test_validation.py @@ -0,0 +1,86 @@ +# Copyright 2015 Hewlett-Packard +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import monasca_api.v2.common.validation as validation + +import unittest + +invalid_chars = "<>={}(),'\"\\;&" + + +class TestMetricNameValidation(unittest.TestCase): + def test_valid_name(self): + metric_name = "this.is_a.valid-name" + validation.metric_name(metric_name) + self.assertTrue(True) + + def test_nonstring_name(self): + metric_name = 123456789 + self.assertRaises(AssertionError, validation.metric_name, metric_name) + + def test_long_name(self): + metric_name = ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") + self.assertRaises(AssertionError, validation.metric_name, metric_name) + + def test_invalid_chars(self): + for c in invalid_chars: + metric_name = "this{}that".format(c) + self.assertRaises(AssertionError, validation.metric_name, metric_name) + + +class TestDimensionValidation(unittest.TestCase): + def test_valid_key(self): + dim_key = "this.is_a.valid-key" + validation.dimension_key(dim_key) + self.assertTrue(True) + + def test_nonstring_key(self): + dim_key = 123456 + self.assertRaises(AssertionError, validation.dimension_key, dim_key) + + def test_long_key(self): + dim_key = ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") + self.assertRaises(AssertionError, validation.dimension_key, dim_key) + + def test_invalid_chars_key(self): + for c in invalid_chars: + dim_key = "this{}that".format(c) + self.assertRaises(AssertionError, validation.dimension_key, dim_key) + + def test_valid_value(self): + dim_value = "this.is_a.valid-value" + validation.dimension_value(dim_value) + self.assertTrue(True) + + def test_nonstring_value(self): + dim_value = None + self.assertRaises(AssertionError, validation.dimension_value, dim_value) + + def test_long_value(self): + dim_value = ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") + self.assertRaises(AssertionError, validation.dimension_value, dim_value) + + def test_invalid_chars_value(self): + for c in invalid_chars: + dim_value = "this{}that".format(c) + self.assertRaises(AssertionError, validation.dimension_value, dim_value) diff --git a/monasca_api/v2/common/validation.py b/monasca_api/v2/common/validation.py new file mode 100644 index 000000000..59b166544 --- /dev/null +++ b/monasca_api/v2/common/validation.py @@ -0,0 +1,36 @@ +# Copyright 2015 Hewlett-Packard +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import re + +invalid_chars = "<>={}(),'\"\\\\;&" +restricted_chars = re.compile('[' + invalid_chars + ']') + + +def metric_name(name): + assert isinstance(name, (str, unicode)), "Metric name must be a string" + assert len(name) <= 255, "Metric name must be 255 characters or less" + assert not restricted_chars.search(name), "Invalid characters in metric name " + name + + +def dimension_key(dkey): + assert isinstance(dkey, (str, unicode)), "Dimension key must be a string" + assert len(dkey) <= 255, "Dimension key must be 255 characters or less" + assert not restricted_chars.search(dkey), "Invalid characters in dimension name " + dkey + + +def dimension_value(value): + assert isinstance(value, (str, unicode)), "Dimension value must be a string" + assert len(value) <= 255, "Dimension value must be 255 characters or less" + assert not restricted_chars.search(value), "Invalid characters in dimension value " + value diff --git a/monasca_api/v2/reference/alarm_definitions.py b/monasca_api/v2/reference/alarm_definitions.py index d98390c21..da8376892 100644 --- a/monasca_api/v2/reference/alarm_definitions.py +++ b/monasca_api/v2/reference/alarm_definitions.py @@ -25,7 +25,7 @@ from monasca_api.common.repositories import exceptions import monasca_api.expression_parser.alarm_expr_parser from monasca_api.v2.common.schemas import ( alarm_definition_request_body_schema as schema_alarms) -from monasca_api.v2.common.schemas import exceptions as schemas_exceptions +from monasca_api.v2.common import validation from monasca_api.v2.reference import alarming from monasca_api.v2.reference import helpers from monasca_api.v2.reference import resource @@ -316,7 +316,11 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API, try: schema_alarms.validate(alarm_definition) - except schemas_exceptions.ValidationException as ex: + if 'match_by' in alarm_definition: + for name in alarm_definition['match_by']: + validation.dimension_key(name) + + except Exception as ex: LOG.debug(ex) raise falcon.HTTPBadRequest('Bad request', ex.message) diff --git a/monasca_api/v2/reference/metrics.py b/monasca_api/v2/reference/metrics.py index 18ae37782..f014e7be0 100644 --- a/monasca_api/v2/reference/metrics.py +++ b/monasca_api/v2/reference/metrics.py @@ -22,6 +22,7 @@ from monasca_api.common.messaging import ( exceptions as message_queue_exceptions) from monasca_api.common.messaging.message_formats import ( metrics as metrics_message) +from monasca_api.v2.common import validation from monasca_api.v2.reference import helpers from monasca_api.v2.reference import resource @@ -80,16 +81,13 @@ class Metrics(metrics_api_v2.MetricsV2API): raise falcon.HTTPBadRequest('Bad request', ex.message) def _validate_single_metric(self, metric): - assert isinstance(metric['name'], (str, unicode)) - assert len(metric['name']) <= 64 - assert isinstance(metric['timestamp'], (int, float)) - assert isinstance(metric['value'], (int, long, float, complex)) + validation.metric_name(metric['name']) + assert isinstance(metric['timestamp'], (int, float)), "Timestamp must be a number" + assert isinstance(metric['value'], (int, long, float, complex)), "Value must be a number" if "dimensions" in metric: - for d in metric['dimensions']: - assert isinstance(d, (str, unicode)) - assert len(d) <= 255 - assert isinstance(metric['dimensions'][d], (str, unicode)) - assert len(metric['dimensions'][d]) <= 255 + for dimension_key in metric['dimensions']: + validation.dimension_key(dimension_key) + validation.dimension_value(metric['dimensions'][dimension_key]) def _send_metrics(self, metrics): diff --git a/test-requirements.txt b/test-requirements.txt index 0217a5963..6fa8e4e99 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,6 +8,7 @@ flake8==2.1.0 pep8<=1.5.6 httplib2>=0.7.5 mock>=1.0 +funcsigs mox>=0.5.3 nose # Docs Requirements diff --git a/tox.ini b/tox.ini index e345842d8..ae782c6a4 100644 --- a/tox.ini +++ b/tox.ini @@ -30,6 +30,15 @@ commands = python setup.py build_sphinx commands = {posargs} [flake8] +max-line-length = 120 +# TODO: ignored checks should be enabled in the future +# H201 no 'except:' at least use 'except Exception:' +# H302 import only modules +# H305 imports not grouped correctly +# H307 like imports should be grouped together +# H405 multi line docstring summary not separated with an empty line +# H904 Wrap long lines in parentheses instead of a backslash +ignore = F821,H201,H302,H305,H307,H405,H904 builtins = _ exclude=.venv,.git,.tox,dist,doc,./monasca_api/openstack/common,*lib/python*,*egg,tools,build show-source = True