Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add start to SSM json schemas #3471

Merged
merged 1 commit into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading