From 8a563c2e76f6a85f0bd6656bc048b86e2efc8a50 Mon Sep 17 00:00:00 2001 From: Kevin DeJong Date: Mon, 21 Oct 2024 09:33:31 -0700 Subject: [PATCH] Add rule E1154 to validate Subnet IDs --- src/cfnlint/rules/formats/SubnetId.py | 33 +++++++++++++++ .../integration/aws-ec2-instance.yaml | 2 +- test/unit/module/cfn_json/test_cfn_json.py | 2 +- test/unit/rules/formats/test_subnet_id.py | 40 +++++++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/cfnlint/rules/formats/SubnetId.py create mode 100644 test/unit/rules/formats/test_subnet_id.py diff --git a/src/cfnlint/rules/formats/SubnetId.py b/src/cfnlint/rules/formats/SubnetId.py new file mode 100644 index 0000000000..f4e5311cf2 --- /dev/null +++ b/src/cfnlint/rules/formats/SubnetId.py @@ -0,0 +1,33 @@ +""" +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: MIT-0 +""" + +from __future__ import annotations + +from typing import Any + +import regex as re + +from cfnlint.jsonschema import Validator +from cfnlint.rules.formats.FormatKeyword import FormatKeyword + + +class SubnetId(FormatKeyword): + id = "E1154" + shortdesc = "Validate VPC subnet id format" + description = "Check that a VPC subnet id matches a pattern" + tags = [] + source_url = "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/format_keyword.md#AWS::EC2::Subnet.Id" + + def __init__(self): + super().__init__(format="AWS::EC2::Subnet.Id") + + def format(self, validator: Validator, instance: Any) -> bool: + if not isinstance(instance, str): + return True + + if re.match(r"^subnet-(([0-9A-Fa-f]{8})|([0-9A-Fa-f]{17}))$", instance): + return True + + return False diff --git a/test/fixtures/templates/integration/aws-ec2-instance.yaml b/test/fixtures/templates/integration/aws-ec2-instance.yaml index 2df4070de1..da3af865ae 100644 --- a/test/fixtures/templates/integration/aws-ec2-instance.yaml +++ b/test/fixtures/templates/integration/aws-ec2-instance.yaml @@ -4,7 +4,7 @@ Resources: Properties: Description: foobar SourceDestCheck: false - SubnetId: subnet-abcdefgh + SubnetId: subnet-0abc1def2345a678b Instance: Type: AWS::EC2::Instance Properties: diff --git a/test/unit/module/cfn_json/test_cfn_json.py b/test/unit/module/cfn_json/test_cfn_json.py index bc285ec789..2463c21e6c 100644 --- a/test/unit/module/cfn_json/test_cfn_json.py +++ b/test/unit/module/cfn_json/test_cfn_json.py @@ -35,7 +35,7 @@ def setUp(self): }, "nat_instance": { "filename": "test/fixtures/templates/quickstart/nat-instance.json", - "failures": 3, + "failures": 4, }, "vpc_management": { "filename": "test/fixtures/templates/quickstart/vpc-management.json", diff --git a/test/unit/rules/formats/test_subnet_id.py b/test/unit/rules/formats/test_subnet_id.py new file mode 100644 index 0000000000..e11a7496ff --- /dev/null +++ b/test/unit/rules/formats/test_subnet_id.py @@ -0,0 +1,40 @@ +""" +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: MIT-0 +""" + +import pytest + +from cfnlint.rules.formats.SubnetId import SubnetId + + +@pytest.fixture(scope="module") +def rule(): + rule = SubnetId() + yield rule + + +@pytest.mark.parametrize( + "name,instance,expected", + [ + ( + "Valid subnet id", + "subnet-abcd1234", + True, + ), + ( + "Valid subnet id long", + "subnet-abcdefa1234567890", + True, + ), + ( + "Valid but wrong type", + [], + True, + ), + ("Invalid subnet ID", "subnet-abc", False), + ], +) +def test_validate(name, instance, expected, rule, validator): + result = rule.format(validator, instance) + assert result == expected, f"Test {name!r} got {result!r}"