From 28d1d762af57393e34e222496b574c253597f149 Mon Sep 17 00:00:00 2001 From: Jordan Reiter Date: Tue, 28 Jul 2020 10:26:59 -0400 Subject: [PATCH] Enforce parameter naming (#3190) * Enforce parameter naming Parameters are not allowed to start with `ssm` or `aws`. This commit adds error messages which correspond exactly to the error messages returned by boto3. * Fix for Python 2 compatibility f-strings not supported in Python 2.7 --- moto/ssm/exceptions.py | 7 ++++ moto/ssm/models.py | 18 +++++++++ tests/test_ssm/test_ssm_boto3.py | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/moto/ssm/exceptions.py b/moto/ssm/exceptions.py index 2e715f16a..f68e47029 100644 --- a/moto/ssm/exceptions.py +++ b/moto/ssm/exceptions.py @@ -78,6 +78,13 @@ class InvalidDocumentOperation(JsonRESTError): ) +class AccessDeniedException(JsonRESTError): + code = 400 + + def __init__(self, message): + super(AccessDeniedException, self).__init__("AccessDeniedException", message) + + class InvalidDocumentContent(JsonRESTError): code = 400 diff --git a/moto/ssm/models.py b/moto/ssm/models.py index 3c29097e8..37d56c2dd 100644 --- a/moto/ssm/models.py +++ b/moto/ssm/models.py @@ -27,6 +27,7 @@ from .exceptions import ( ParameterNotFound, DocumentAlreadyExists, InvalidDocumentOperation, + AccessDeniedException, InvalidDocument, InvalidDocumentContent, InvalidDocumentVersion, @@ -1254,6 +1255,23 @@ class SimpleSystemManagerBackend(BaseBackend): def put_parameter( self, name, description, value, type, allowed_pattern, keyid, overwrite ): + if name.lower().lstrip("/").startswith("aws") or name.lower().lstrip( + "/" + ).startswith("ssm"): + is_path = name.count("/") > 1 + if name.lower().startswith("/aws") and is_path: + raise AccessDeniedException( + "No access to reserved parameter name: {name}.".format(name=name) + ) + if not is_path: + invalid_prefix_error = 'Parameter name: can\'t be prefixed with "aws" or "ssm" (case-insensitive).' + else: + invalid_prefix_error = ( + 'Parameter name: can\'t be prefixed with "ssm" (case-insensitive). ' + "If formed as a path, it can consist of sub-paths divided by slash symbol; each sub-path can be " + "formed as a mix of letters, numbers and the following 3 symbols .-_" + ) + raise ValidationException(invalid_prefix_error) previous_parameter_versions = self._parameters[name] if len(previous_parameter_versions) == 0: previous_parameter = None diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index e899613e0..9715866e9 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -299,6 +299,73 @@ def test_put_parameter(): ) +@mock_ssm +def test_put_parameter_invalid_names(): + client = boto3.client("ssm", region_name="us-east-1") + + invalid_prefix_err = ( + 'Parameter name: can\'t be prefixed with "aws" or "ssm" (case-insensitive).' + ) + + client.put_parameter.when.called_with( + Name="ssm_test", Value="value", Type="String" + ).should.throw( + ClientError, invalid_prefix_err, + ) + + client.put_parameter.when.called_with( + Name="SSM_TEST", Value="value", Type="String" + ).should.throw( + ClientError, invalid_prefix_err, + ) + + client.put_parameter.when.called_with( + Name="aws_test", Value="value", Type="String" + ).should.throw( + ClientError, invalid_prefix_err, + ) + + client.put_parameter.when.called_with( + Name="AWS_TEST", Value="value", Type="String" + ).should.throw( + ClientError, invalid_prefix_err, + ) + + ssm_path = "/ssm_test/path/to/var" + client.put_parameter.when.called_with( + Name=ssm_path, Value="value", Type="String" + ).should.throw( + ClientError, + 'Parameter name: can\'t be prefixed with "ssm" (case-insensitive). If formed as a path, it can consist of ' + "sub-paths divided by slash symbol; each sub-path can be formed as a mix of letters, numbers and the following " + "3 symbols .-_", + ) + + ssm_path = "/SSM/PATH/TO/VAR" + client.put_parameter.when.called_with( + Name=ssm_path, Value="value", Type="String" + ).should.throw( + ClientError, + 'Parameter name: can\'t be prefixed with "ssm" (case-insensitive). If formed as a path, it can consist of ' + "sub-paths divided by slash symbol; each sub-path can be formed as a mix of letters, numbers and the following " + "3 symbols .-_", + ) + + aws_path = "/aws_test/path/to/var" + client.put_parameter.when.called_with( + Name=aws_path, Value="value", Type="String" + ).should.throw( + ClientError, "No access to reserved parameter name: {}.".format(aws_path), + ) + + aws_path = "/AWS/PATH/TO/VAR" + client.put_parameter.when.called_with( + Name=aws_path, Value="value", Type="String" + ).should.throw( + ClientError, "No access to reserved parameter name: {}.".format(aws_path), + ) + + @mock_ssm def test_put_parameter_china(): client = boto3.client("ssm", region_name="cn-north-1")