Beispiel #1
0
 def template(self):
     dir_path = os.path.dirname(os.path.realpath(__file__))
     cf_script = open(
         '{}/test_templates/sqs_queue_with_wildcards.json'.format(dir_path))
     cf_template = S3Adapter().convert_json_or_yaml_to_dict(
         cf_script.read())
     return pycfmodel.parse(cf_template)
Beispiel #2
0
    def test_invalid_url(self):
        url = "someweirdurl/wheretheresnoprotocol/justcoz"

        adapter = S3Adapter()

        with pytest.raises(InvalidURLException):
            result = adapter.download_template_to_dictionary(url)
 def template(self):
     dir_path = os.path.dirname(os.path.realpath(__file__))
     cf_script = open(
         "{}/test_templates/s3_bucket_cross_account.json".format(dir_path))
     cf_template = S3Adapter().convert_json_or_yaml_to_dict(
         cf_script.read())
     return pycfmodel.parse(cf_template)
Beispiel #4
0
    def test_parse_slingshot_support_file_url(self):
        url = "https://s3-eu-west-1.amazonaws.com/skyscanner-prod-eu-west-1-slingshot-deployment/statemachine-execution/iac/20180227114824_he6Fr31y/lambda_role.yml?Expires=1519735705&AWSAccessKeyId=ASIAJWMEHG7LG45YJUMA&Signature=A%2BW9r7RKdcVjO2C9cDI1sLCJE8c%3D&x-amz-security-token=FQoDYXdzELj%2F%2F%2F%2F%2F%2F..."

        bucket, path = S3Adapter.extract_bucket_name_and_path_from_url(url)

        assert bucket == 'skyscanner-prod-eu-west-1-slingshot-deployment'
        assert path == 'statemachine-execution/iac/20180227114824_he6Fr31y/lambda_role.yml'
Beispiel #5
0
    def test_valid_yaml_with_cf_shorthand_join(self):
        yaml_content = """
        myprop: !Join ['-', ['hello', 'world']]\n\r
        """

        bucket = 'cf-templates-1234'
        filename = 'myexamplestack.yml'

        self.set_s3_file(bucket, filename, yaml_content)

        url = "https://s3-eu-west-1.amazonaws.com/{}/{}".format(
            bucket, filename)

        adapter = S3Adapter()
        result = adapter.download_template_to_dictionary(url)

        assert result['myprop'] == {'Fn::Join': ['-', ['hello', 'world']]}
Beispiel #6
0
    def test_valid_yaml(self):
        yaml_content = """
        hello: this is valid\n\r
        """

        bucket = 'cf-templates-1234'
        filename = 'myexamplestack.yml'

        self.set_s3_file(bucket, filename, yaml_content)

        url = "https://s3-eu-west-1.amazonaws.com/{}/{}".format(
            bucket, filename)

        adapter = S3Adapter()
        result = adapter.download_template_to_dictionary(url)

        assert result['hello'] == "this is valid"
Beispiel #7
0
    def test_valid_yaml_with_cf_getatt_dotsyntax(self):
        yaml_content = """
        myprop: !GetAtt hello.world\n\r
        """

        bucket = 'cf-templates-1234'
        filename = 'myexamplestack.yml'

        self.set_s3_file(bucket, filename, yaml_content)

        url = "https://s3-eu-west-1.amazonaws.com/{}/{}".format(
            bucket, filename)

        adapter = S3Adapter()
        result = adapter.download_template_to_dictionary(url)

        assert result['myprop'] == {'Fn::GetAtt': ['hello', 'world']}
Beispiel #8
0
    def test_urlencoded_url(self):
        json_content = """
            {
                "hello": "this is valid json"
            }
        """
        bucket = 'cf-templates-1234'
        filename = '2017284ltK-adfs%20iam%20role.json'

        self.set_s3_file(bucket, '2017284ltK-adfs iam role.json', json_content)

        url = "https://s3-eu-west-1.amazonaws.com/{}/{}".format(
            bucket, filename)

        adapter = S3Adapter()
        result = adapter.download_template_to_dictionary(url)

        assert result['hello'] == "this is valid json"
Beispiel #9
0
    def test_url_with_path_prefix(self):
        json_content = """
            {
                "hello": "this is valid json"
            }
        """
        bucket = 'cf-templates-1234'
        filename = 'myprefixes/aaaaaa/myexamplestack.json'

        self.set_s3_file(bucket, filename, json_content)

        url = "https://s3-eu-west-1.amazonaws.com/{}/{}".format(
            bucket, filename)

        adapter = S3Adapter()
        result = adapter.download_template_to_dictionary(url)

        assert result['hello'] == "this is valid json"
Beispiel #10
0
    def test_invalid_format(self):
        invalid_content = """
            {
                'asdf': 'asdf'
                'asd': 'asd'
        """

        bucket = 'cf-templates-1234'
        filename = 'myexamplestack.yml'

        self.set_s3_file(bucket, filename, invalid_content)

        url = "https://s3-eu-west-1.amazonaws.com/{}/{}".format(
            bucket, filename)

        adapter = S3Adapter()

        assert adapter.download_template_to_dictionary(url) is None
Beispiel #11
0
def test_script(script_name, service_name, project_name):
    event = {
        'stack_template_url': 'https://fake/bucket/key',
        'project': project_name,
        'serviceName': service_name,
    }
    cf_script = open('{}/test_cf_scripts/{}'.format(dir_path, script_name))

    mock_created_s3_adapter_object = Mock()
    mock_created_s3_adapter_object.download_template_to_dictionary.return_value = S3Adapter(
    ).convert_json_or_yaml_to_dict(cf_script.read())

    mock_s3_adapter = Mock(return_value=mock_created_s3_adapter_object)

    cf_script.close()

    with patch('cfripper.main.S3Adapter', new=mock_s3_adapter):
        from cfripper.main import handler
        event_result = handler(event, None)
        print('{} -- valid: {}\n {}'.format(script_name, event_result['valid'],
                                            event_result['reason']))
Beispiel #12
0
    def test_with_templates(self):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        test_templates = glob.glob('{}/test_templates/*.*'.format(dir_path))
        for template in test_templates:
            cf_script = open(template)
            cf_template = S3Adapter().convert_json_or_yaml_to_dict(
                cf_script.read())

            config = Config(
                project_name=template,
                service_name=template,
                stack_name=template,
                rules=ALL_RULES.keys(),
            )

            # Scan result
            result = Result()

            rules = [
                ALL_RULES.get(rule)(config, result) for rule in config.RULES
            ]
            processor = RuleProcessor(*rules)

            processor.process_cf_template(cf_template, config, result)
            # Use this to print the stack if there's an error
            if len(result.exceptions):
                print(template)
                traceback.print_tb(result.exceptions[0].__traceback__)

            no_resource_templates = [
                'vulgar_bad_syntax.yml',
                'rubbish.json',
            ]
            if template.split('/')[-1] in no_resource_templates:
                assert len(result.exceptions) == 1
            else:
                assert len(result.exceptions) == 0
Beispiel #13
0
def handler(event, context):
    """
    Main entry point of the Lambda function.

    :param event:
    Request JSON format for proxy integration
    {
    	"resource": "Resource path",
    	"path": "Path parameter",
    	"httpMethod": "Incoming request's method name"
    	"headers": {Incoming request headers}
    	"queryStringParameters": {"stack_template_url": String }
    	"pathParameters":  {path parameters}
    	"stageVariables": {Applicable stage variables}
    	"requestContext": {Request context, including authorizer-returned key-value pairs}
    	"body": "A JSON string of the request payload."
    	"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
    }
    :param context:
    :return:
    Response JSON format
    {
    	"isBase64Encoded": true|false,
    	"statusCode": httpStatusCode,
    	"headers": { "headerName": "headerValue", ... },
    	"body": "..."
    }

    """

    print(event)
    qp = event.get("queryStringParameters")
    if not qp.get("stack_template_url"):
        raise ValueError(
            "Invalid event type: no parameter 'stack_template_url' in request."
        )

    result = Result()

    s3 = S3Adapter()
    template = s3.download_template_to_dictionary(qp["stack_template_url"])
    if not template:
        # In case of an ivalid script log a warning and return early
        result.add_exception(
            TypeError("Malformated CF script: {}".format(
                qp["stack_template_url"])))
        return {
            "isBase64Encoded":
            False,
            "statusCode":
            400,
            "headers": {},
            "body":
            str({
                "valid": "false",
                "reason": '',
                "failed_rules": [],
                "exceptions": [x.args[0] for x in result.exceptions],
            })
        }

    # Process Rules
    config = Config(
        project_name=event.get("project"),
        service_name=event.get("serviceName"),
        stack_name=event.get("stack", {}).get("name"),
        rules=ALL_RULES.keys(),
        event=event.get("event"),
        template_url=event.get("stack_template_url"),
    )

    logger.info("Scan started for: {}; {}; {};".format(
        config.project_name,
        config.service_name,
        config.stack_name,
    ))

    rules = [ALL_RULES.get(rule)(config, result) for rule in config.RULES]
    processor = RuleProcessor(*rules)

    processor.process_cf_template(template, config, result)

    if not result.valid:
        log_results(
            "Failed rules",
            config.project_name,
            config.service_name,
            config.stack_name,
            result.failed_rules,
            result.warnings,
            qp["stack_template_url"],
        )
        logger.info("FAIL: {}; {}; {}".format(
            config.project_name,
            config.service_name,
            config.stack_name,
        ))
    else:
        logger.info("PASS: {}; {}; {}".format(
            config.project_name,
            config.service_name,
            config.stack_name,
        ))
    if len(result.failed_monitored_rules) > 0 or len(result.warnings) > 0:
        log_results(
            "Failed monitored rules",
            config.project_name,
            config.service_name,
            config.stack_name,
            result.failed_monitored_rules,
            result.warnings,
            qp["stack_template_url"],
        )
    # TODO base64 encode and implement more error code responses
    return {
        "isBase64Encoded":
        False,
        "statusCode":
        200,
        "headers": {},
        "body":
        str({
            "valid":
            str(result.valid).lower(),
            "reason":
            ",".join([
                "{}-{}".format(r["rule"], r["reason"])
                for r in result.failed_rules
            ]),
            "failed_rules":
            result.failed_rules,
            "exceptions": [x.args[0] for x in result.exceptions],
            "warnings":
            result.failed_monitored_rules,
        })
    }
Beispiel #14
0
def handler(event, context):
    """
    Main entry point of the Lambda function.

    :param event: {
        "stack_template_url": String
    }
    :param context:
    :return:
    """
    if not event.get("stack_template_url"):
        raise ValueError(
            "Invalid event type: no parameter 'stack_template_url' in request."
        )

    result = Result()

    s3 = S3Adapter()
    template = s3.download_template_to_dictionary(event["stack_template_url"])
    if not template:
        # In case of an ivalid script log a warning and return early
        result.add_exception(
            TypeError("Malformated CF script: {}".format(
                event["stack_template_url"])))
        return {
            "valid": "true",
            "reason": '',
            "failed_rules": [],
            "exceptions": [x.args[0] for x in result.exceptions],
        }

    # Process Rules
    config = Config(
        project_name=event.get("project"),
        service_name=event.get("serviceName"),
        stack_name=event.get("stack", {}).get("name"),
        rules=ALL_RULES.keys(),
    )

    logger.info("Scan started for: {}; {}; {};".format(
        config.project_name,
        config.service_name,
        config.stack_name,
    ))

    rules = [ALL_RULES.get(rule)(config, result) for rule in config.RULES]
    processor = RuleProcessor(*rules)

    processor.process_cf_template(template, config, result)

    if not result.valid:
        log_results(
            "Failed rules",
            config.project_name,
            config.service_name,
            config.stack_name,
            result.failed_rules,
            result.warnings,
            event["stack_template_url"],
        )
        logger.info("FAIL: {}; {}; {}".format(
            config.project_name,
            config.service_name,
            config.stack_name,
        ))
    else:
        logger.info("PASS: {}; {}; {}".format(
            config.project_name,
            config.service_name,
            config.stack_name,
        ))
    if len(result.failed_monitored_rules) > 0 or len(result.warnings) > 0:
        log_results(
            "Failed monitored rules",
            config.project_name,
            config.service_name,
            config.stack_name,
            result.failed_monitored_rules,
            result.warnings,
            event["stack_template_url"],
        )
    return {
        "valid":
        str(result.valid).lower(),
        "reason":
        ",".join([
            "{}-{}".format(r["rule"], r["reason"]) for r in result.failed_rules
        ]),
        "failed_rules":
        result.failed_rules,
        "exceptions": [x.args[0] for x in result.exceptions],
        "warnings":
        result.failed_monitored_rules,
    }