Skip to content

Commit

Permalink
Add start to SSM json schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
kddejong committed Jul 7, 2024
1 parent 78e9fa9 commit 73f7d57
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 0 deletions.
Empty file.
27 changes: 27 additions & 0 deletions src/cfnlint/data/schemas/other/ssm/document.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": true,
"properties": {
"assumeRole": {
"type": "string"
},
"description": {
"type": "string"
},
"schemaVersion": {
"description": "The schema version to use.",
"enum": [
"0.3",
"1.2",
"2.0",
"2.2"
],
"type": "string"
}
},
"required": [
"schemaVersion"
],
"title": "JSON schema for AWS Automation Documents",
"type": "object"
}
84 changes: 84 additions & 0 deletions src/cfnlint/rules/resources/ssm/Document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""

from __future__ import annotations

from typing import Any

import cfnlint.data.schemas.other.ssm
from cfnlint.decode import decode_str
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
from cfnlint.rules.jsonschema.CfnLintJsonSchema import CfnLintJsonSchema, SchemaDetails
from cfnlint.schema.resolver import RefResolver


class Document(CfnLintJsonSchema):
id = "E3051"
shortdesc = "Validate the structure of a SSM document"
description = (
"SSM documents are nested JSON/YAML in CloudFormation "
"this rule adds validation to those documents"
)
source_url = "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html"
tags = ["properties", "ssm", "document"]

def __init__(self):
super().__init__(
["Resources/AWS::SSM::Document/Properties/Content"],
schema_details=SchemaDetails(
module=cfnlint.data.schemas.other.ssm,
filename="document.json",
),
)

store = {
"document": self.schema,
}

self.resolver = RefResolver.from_schema(self.schema, store=store)

# pylint: disable=unused-argument
def validate(
self,
validator: Validator,
_: Any,
instance: Any,
schema: dict[str, Any],
) -> ValidationResult:
# First time child rules are configured against the rule
# so we can run this now

if validator.is_type(instance, "string"):
ssm_validator = validator.evolve(
context=validator.context.evolve(
functions=[],
strict_types=True,
),
resolver=self.resolver,
schema=self.schema,
)

instance, errs = decode_str(instance)
if errs:
yield ValidationError(
"Document is not of type 'object'",
validator="type",
rule=self,
)
return
else:
ssm_validator = validator.evolve(
cfn=validator.cfn,
context=validator.context.evolve(
strict_types=True,
),
resolver=self.resolver,
schema=self.schema,
)

for err in ssm_validator.iter_errors(instance):
if not err.validator.startswith("fn_") and err.validator not in ["cfnLint"]:
err.rule = self
yield err
Empty file.
Empty file.
128 changes: 128 additions & 0 deletions test/unit/rules/resources/ssm/test_document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""

from collections import deque

import pytest

from cfnlint.jsonschema.validators import ValidationError
from cfnlint.rules.resources.ssm.Document import Document


@pytest.fixture(scope="module")
def rule():
rule = Document()
yield rule


@pytest.mark.parametrize(
"name,document,expected",
[
(
"Valid string yaml",
"""
schemaVersion: "2.2"
mainSteps:
- action: aws:runShellScript
""",
[],
),
(
"Valid string json",
"""
{
"schemaVersion": "2.2",
"mainSteps": [
{
"action": "aws:runShellScript"
}
]
}
""",
[],
),
(
"Valid object",
{
"schemaVersion": "2.2",
"mainSteps": [
{"action": "aws:runShellScript"},
],
},
[],
),
(
"InValid string yaml",
"""
schemaVersion: 2.2
mainSteps:
- action: aws:runShellScript
""",
[
ValidationError(
"2.2 is not of type 'string'",
rule=Document(),
validator="type",
schema_path=deque(["properties", "schemaVersion", "type"]),
path=deque(["schemaVersion"]),
)
],
),
(
"Not a valid json or yaml object",
"arn:aws-us-gov:iam::123456789012:role/test",
[
ValidationError(
"Document is not of type 'object'",
rule=Document(),
validator="type",
schema_path=deque([]),
path=deque([]),
)
],
),
(
"Invalid schema version in object",
{
"schemaVersion": 2.2,
"mainSteps": [
{"action": "aws:runShellScript"},
],
},
[
ValidationError(
"2.2 is not of type 'string'",
rule=Document(),
validator="type",
schema_path=deque(["properties", "schemaVersion", "type"]),
path=deque(["schemaVersion"]),
)
],
),
(
"Invalid type",
[],
[
ValidationError(
"[] is not of type 'object'",
rule=Document(),
validator="type",
schema_path=deque(["type"]),
path=deque([]),
)
],
),
],
)
def test_validate(
name,
document,
expected,
rule,
validator,
):
errs = list(rule.validate(validator, {}, document, {}))

assert errs == expected

0 comments on commit 73f7d57

Please sign in to comment.