Пример #1
0
def test_only_whitelisted_resources_are_removed(mock_rule_to_resource_whitelist):
    config = Config(
        stack_name="otherstack",
        rules=["S3CrossAccountTrustRule"],
        rule_to_resource_whitelist=mock_rule_to_resource_whitelist,
    )

    result = Result()
    failed_rules = [
        Failure(
            rule="S3CrossAccountTrustRule",
            reason="Forbidden cross-account policy allow with 123456789 for an S3 bucket.",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids={"rolething", "thenotwhitelistedthing", "anotherone"},
            actions=None,
            granularity=RuleGranularity.RESOURCE,
        )
    ]
    result.failed_rules = failed_rules

    RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result)
    assert result.failed_rules == [
        Failure(
            rule="S3CrossAccountTrustRule",
            reason="Forbidden cross-account policy allow with 123456789 for an S3 bucket.",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids={"thenotwhitelistedthing", "anotherone"},
            actions=None,
            granularity=RuleGranularity.RESOURCE,
        )
    ]
Пример #2
0
def test_action_whitelist_keeps_non_whitelisted_actions():
    whitelist_for_all_stacks = {"MockRule": {".*": {"s3:List"}}}
    config = Config(stack_name="abcd", rules=["MockRule"], rule_to_action_whitelist=whitelist_for_all_stacks)

    result = Result()
    failed_rules = [
        Failure(
            rule="MockRule",
            reason="MockRule is invalid for some actions",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            actions={"s3:ListBucket", "s3:GetBucket"},
            granularity=RuleGranularity.ACTION,
        )
    ]
    result.failed_rules = failed_rules

    RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result)
    assert result.failed_rules == [
        Failure(
            rule="MockRule",
            reason="MockRule is invalid for some actions",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            actions={"s3:GetBucket"},
            granularity=RuleGranularity.ACTION,
        )
    ]
Пример #3
0
    def test_with_templates(self):
        dir_path = os.path.dirname(os.path.realpath(__file__))

        test_templates = glob.glob(f"{dir_path}/test_templates/*.*")
        for template in test_templates:
            with open(template) as cf_script:
                cf_template = convert_json_or_yaml_to_dict(cf_script.read())

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

            # Scan result
            result = Result()

            rules = [DEFAULT_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
Пример #4
0
def test_remove_failures_from_whitelisted_resources_only_removes_resource_granularity(mock_rule_to_resource_whitelist):
    config = Config(
        stack_name="otherstack",
        rules=["S3CrossAccountTrustRule"],
        rule_to_resource_whitelist=mock_rule_to_resource_whitelist,
    )

    result = Result()
    failed_rules = [
        {
            "rule": "S3CrossAccountTrustRule",
            "reason": "rolething has forbidden cross-account policy allow with 123456789 for an S3 bucket.",
            "rule_mode": RuleMode.BLOCKING,
            "risk_value": RuleRisk.HIGH,
            "resource_ids": {"rolething"},
            "actions": None,
            "granularity": RuleGranularity.ACTION,
        },
        {
            "rule": "S3CrossAccountTrustRule",
            "reason": "anotherthing has forbidden cross-account policy allow with 123456789 for an S3 bucket.",
            "rule_mode": RuleMode.BLOCKING,
            "risk_value": RuleRisk.HIGH,
            "resource_ids": {"anotherthing"},
            "actions": None,
            "granularity": RuleGranularity.RESOURCE,
        }
    ]
    result.failed_rules = failed_rules

    RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result)
    assert result.failed_rules == failed_rules
Пример #5
0
def test_with_no_rules(mock_remove_whitelisted_actions, mock_remove_whitelisted_resources, template):
    processor = RuleProcessor()
    config = Mock()
    result = Result()

    processor.process_cf_template(template, config, result)
    mock_remove_whitelisted_actions.assert_called()
    mock_remove_whitelisted_resources.assert_called()
Пример #6
0
def test_with_mock_rule(template):
    rule = Mock()

    processor = RuleProcessor(rule)

    config = Mock()
    result = Result()
    processor.process_cf_template(template, config, result)

    rule.invoke.assert_called()
Пример #7
0
    def test_with_mock_rule(self):
        rule = Mock()

        processor = RuleProcessor(rule)

        config = Mock()
        result = Mock()
        processor.process_cf_template(EXAMPLE_CF_TEMPLATE, config, result)

        rule.invoke.assert_called()
Пример #8
0
def test_can_whitelist_resource_from_any_stack_if_granularity_is_resource():

    whitelist_for_all_stacks = {
        "S3CrossAccountTrustRule": {
            ".*": {
                "ProductionAccessTest",
            },
            "otherstack": {
                "rolething",
            }
        },
    }
    config = Config(
        stack_name="abcd",
        rules=["S3CrossAccountTrustRule"],
        rule_to_resource_whitelist=whitelist_for_all_stacks,
    )

    result = Result()
    failed_rules = [
        {
            "rule": "S3CrossAccountTrustRule",
            "reason": "ProductionAccessTest has forbidden cross-account policy allow with 123456789 for an S3 bucket.",
            "rule_mode": RuleMode.BLOCKING,
            "risk_value": RuleRisk.HIGH,
            "resource_ids": {"ProductionAccessTest"},
            "actions": None,
            "granularity": RuleGranularity.RESOURCE,
        },
        {
            "rule": "S3CrossAccountTrustRule",
            "reason": "This one isn't whitelisted because granularity is ACTION and not RESOURCE",
            "rule_mode": RuleMode.BLOCKING,
            "risk_value": RuleRisk.HIGH,
            "resource_ids": {"ProductionAccessTest"},
            "actions": None,
            "granularity": RuleGranularity.ACTION,
        },
    ]
    result.failed_rules = failed_rules

    RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result)
    assert result.failed_rules == [{
        "rule": "S3CrossAccountTrustRule",
        "reason": "This one isn't whitelisted because granularity is ACTION and not RESOURCE",
        "rule_mode": RuleMode.BLOCKING,
        "risk_value": RuleRisk.HIGH,
        "resource_ids": {"ProductionAccessTest"},
        "actions": None,
        "granularity": RuleGranularity.ACTION,
    }]
Пример #9
0
def test_remove_debug_rules():
    original_failed_monitored_rules = [
        Failure(
            rule="a",
            reason="something",
            rule_mode=RuleMode.MONITOR,
            granularity=RuleGranularity.STACK,
            risk_value=RuleRisk.HIGH,
        ),
        Failure(
            rule="b",
            reason="something",
            rule_mode=RuleMode.DEBUG,
            granularity=RuleGranularity.STACK,
            risk_value=RuleRisk.MEDIUM,
        ),
        Failure(
            rule="c",
            reason="something",
            rule_mode=RuleMode.MONITOR,
            granularity=RuleGranularity.STACK,
            risk_value=RuleRisk.LOW,
        ),
    ]

    list_with_no_debug_rules = [original_failed_monitored_rules[0], original_failed_monitored_rules[2]]

    processed_list = RuleProcessor.remove_debug_rules(rules=original_failed_monitored_rules)
    assert list_with_no_debug_rules == processed_list
Пример #10
0
def test_whitelisted_stacks_do_not_report_anything(template_two_roles_dict):
    result = Result()
    mock_stack_whitelist = {"mockstack": ["CrossAccountTrustRule"]}
    mock_config = Config(
        rules=["CrossAccountTrustRule"],
        aws_account_id="123456789",
        stack_name="mockstack",
        stack_whitelist=mock_stack_whitelist,
    )
    rules = [
        DEFAULT_RULES.get(rule)(mock_config, result)
        for rule in mock_config.rules
    ]
    processor = RuleProcessor(*rules)
    processor.process_cf_template(template_two_roles_dict, mock_config, result)

    assert result.valid
Пример #11
0
def test_non_whitelisted_stacks_are_reported_normally(
        template_two_roles_dict, expected_result_two_roles):
    result = Result()
    mock_stack_whitelist = {"mockstack": ["CrossAccountTrustRule"]}
    mock_config = Config(
        rules=["CrossAccountTrustRule"],
        aws_account_id="123456789",
        stack_name="anotherstack",
        stack_whitelist=mock_stack_whitelist,
    )
    rules = [
        DEFAULT_RULES.get(rule)(mock_config, result)
        for rule in mock_config.rules
    ]
    processor = RuleProcessor(*rules)
    processor.process_cf_template(template_two_roles_dict, mock_config, result)
    assert not result.valid
    assert result.failed_rules == expected_result_two_roles
Пример #12
0
def test_remove_failures_from_whitelisted_actions_only_removes_action_granularity(mock_rule_to_action_whitelist):
    config = Config(
        stack_name="teststack",
        rules=["S3CrossAccountTrustRule"],
        rule_to_action_whitelist=mock_rule_to_action_whitelist,
    )

    result = Result()
    failed_rules = [
        Failure(
            rule="WildcardResourceRule",
            reason="rolething is using a wildcard resource in BucketAccessPolicy",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids={"BucketAccessPolicy"},
            actions={"s3:Get*"},
            granularity=RuleGranularity.ACTION,
        ),
        Failure(
            rule="WildcardResourceRule",
            reason="rolething is using a wildcard resource in BucketAccessPolicy",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids=set(),
            actions=set(),
            granularity=RuleGranularity.STACK,
        ),
    ]
    result.failed_rules = failed_rules

    RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result)
    assert result.failed_rules == [
        Failure(
            rule="WildcardResourceRule",
            reason="rolething is using a wildcard resource in BucketAccessPolicy",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids=set(),
            actions=set(),
            granularity=RuleGranularity.STACK,
        )
    ]
Пример #13
0
def test_remove_failures_from_whitelisted_actions_failure_no_actions_is_removed(mock_logger, mock_rule_to_action_whitelist):
    config = Config(
        stack_name="teststack",
        rules=["S3CrossAccountTrustRule"],
        rule_to_action_whitelist=mock_rule_to_action_whitelist,
    )

    result = Result()
    failure = {
        "rule": "S3CrossAccountTrustRule",
        "reason": "rolething has forbidden cross-account policy allow with 123456789 for an S3 bucket.",
        "rule_mode": RuleMode.BLOCKING,
        "risk_value": RuleRisk.HIGH,
        "actions": set(),
        "granularity": RuleGranularity.ACTION,
    }
    result.failed_rules = [failure]

    RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result)
    assert result.failed_rules == []
    mock_logger.assert_called_once_with(f"Failure with action granularity doesn't have actions: {failure}")
Пример #14
0
def test_remove_failures_from_whitelisted_actions_uses_whitelist(mock_rule_to_action_whitelist):

    config = Config(
        stack_name="teststack", rules=["WildcardResourceRule"], rule_to_action_whitelist=mock_rule_to_action_whitelist
    )

    result = Result()
    result.failed_rules = [
        Failure(
            rule="WildcardResourceRule",
            reason="rolething is using a wildcard resource in BucketAccessPolicy",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids={"BucketAccessPolicy"},
            actions={"s3:Get*"},
            granularity=RuleGranularity.ACTION,
        ),
        Failure(
            rule="WildcardResourceRule",
            reason="rolething is using a wildcard resource in DynamoAccessPolicy",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids={"DynamoAccessPolicy"},
            actions={"dynamodb:Get"},
            granularity=RuleGranularity.ACTION,
        ),
    ]

    RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result)
    assert result.failed_rules == [
        Failure(
            rule="WildcardResourceRule",
            reason="rolething is using a wildcard resource in DynamoAccessPolicy",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            resource_ids={"DynamoAccessPolicy"},
            actions={"dynamodb:Get"},
            granularity=RuleGranularity.ACTION,
        )
    ]
Пример #15
0
def test_can_whitelist_action_from_any_stack_if_granularity_is_action():

    whitelist_for_all_stacks = {"S3CrossAccountTrustRule": {".*": {"s3:ListBucket"}}}
    config = Config(
        stack_name="abcd", rules=["S3CrossAccountTrustRule"], rule_to_action_whitelist=whitelist_for_all_stacks
    )

    result = Result()
    failed_rules = [
        Failure(
            rule="S3CrossAccountTrustRule",
            reason="ProductionAccessTest has forbidden cross-account policy allow with 123456789 for an S3 bucket.",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            actions={"s3:ListBucket"},
            granularity=RuleGranularity.ACTION,
        ),
        Failure(
            rule="S3CrossAccountTrustRule",
            reason="This one isn't whitelisted because granularity is STACK and not ACTION",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            actions=set(),
            granularity=RuleGranularity.STACK,
        ),
    ]
    result.failed_rules = failed_rules

    RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result)
    assert result.failed_rules == [
        Failure(
            rule="S3CrossAccountTrustRule",
            reason="This one isn't whitelisted because granularity is STACK and not ACTION",
            rule_mode=RuleMode.BLOCKING,
            risk_value=RuleRisk.HIGH,
            actions=set(),
            granularity=RuleGranularity.STACK,
        )
    ]
Пример #16
0
def test_remove_failures_from_whitelisted_resources_failure_no_resources_is_removed(
    mock_logger, mock_rule_to_resource_whitelist
):
    config = Config(
        stack_name="otherstack",
        rules=["S3CrossAccountTrustRule"],
        rule_to_resource_whitelist=mock_rule_to_resource_whitelist,
    )

    result = Result()
    failure = Failure(
        rule="S3CrossAccountTrustRule",
        reason="rolething has forbidden cross-account policy allow with 123456789 for an S3 bucket.",
        rule_mode=RuleMode.BLOCKING,
        risk_value=RuleRisk.HIGH,
        actions=None,
        granularity=RuleGranularity.RESOURCE,
    )
    result.failed_rules = [failure]

    RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result)
    assert result.failed_rules == []
    mock_logger.assert_called_once_with(f"Failure with resource granularity doesn't have resources: {failure}")
Пример #17
0
def test_remove_debug_rules():
    original_failed_monitored_rules = [
        {"rule": "a", "reason": "something", "rule_mode": RuleMode.MONITOR, "risk_value": RuleRisk.HIGH},
        {"rule": "b", "reason": "something", "rule_mode": RuleMode.DEBUG, "risk_value": RuleRisk.MEDIUM},
        {"rule": "c", "reason": "something", "rule_mode": RuleMode.MONITOR, "risk_value": RuleRisk.LOW},
    ]

    list_with_no_debug_rules = [
        {"rule": "a", "reason": "something", "rule_mode": RuleMode.MONITOR, "risk_value": RuleRisk.HIGH},
        {"rule": "c", "reason": "something", "rule_mode": RuleMode.MONITOR, "risk_value": RuleRisk.LOW},
    ]

    processed_list = RuleProcessor.remove_debug_rules(rules=original_failed_monitored_rules)
    assert list_with_no_debug_rules == processed_list
Пример #18
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
Пример #19
0
def test_resource_whitelisting_works_as_expected(template_two_roles_dict,
                                                 expected_result_two_roles):
    result = Result()
    mock_rule_to_resource_whitelist = {
        "CrossAccountTrustRule": {
            ".*": {"RootRoleOne"}
        }
    }
    mock_config = Config(
        rules=["CrossAccountTrustRule"],
        aws_account_id="123456789",
        rule_to_resource_whitelist=mock_rule_to_resource_whitelist,
        stack_name="mockstack",
        stack_whitelist={},
    )
    rules = [
        DEFAULT_RULES.get(rule)(mock_config, result)
        for rule in mock_config.rules
    ]
    processor = RuleProcessor(*rules)
    processor.process_cf_template(template_two_roles_dict, mock_config, result)

    assert not result.valid
    assert result.failed_rules == expected_result_two_roles[-2:]
Пример #20
0
def test_with_templates(cf_path):
    with open(cf_path) as cf_script:
        cf_template = convert_json_or_yaml_to_dict(cf_script.read())

    config = Config(project_name=cf_path,
                    service_name=cf_path,
                    stack_name=cf_path,
                    rules=DEFAULT_RULES.keys())

    # Scan result
    result = Result()

    cfmodel = pycfmodel.parse(cf_template).resolve()

    rules = [DEFAULT_RULES.get(rule)(config, result) for rule in config.rules]
    processor = RuleProcessor(*rules)
    processor.process_cf_template(cfmodel, config, result)

    # Use this to print the stack if there'IAMManagedPolicyWildcardActionRule an error
    if len(result.exceptions):
        print(cf_path)
        traceback.print_tb(result.exceptions[0].__traceback__)

    assert len(result.exceptions) == 0
Пример #21
0
def handler(event, context):
    """
    Main entry point of the Lambda function.

    :param event: {
        "stack_template_url": String,
        "project": String,
        "stack": {
            "name": String,
        },
        "event": String,
        "region": String,
        "account": {
            "name": String,
            "id": String,
        },
        "user_agent": String,
    }
    :param context:
    :return:
    """

    setup_logging()
    if not event.get("stack_template_url") and not event.get("stack",
                                                             {}).get("name"):
        raise ValueError(
            "Invalid event type: no parameter 'stack_template_url' or 'stack::name' in request."
        )

    result = Result()
    template = get_template(event)

    if not template:
        # In case of an invalid script log a warning and return early
        result.add_exception(
            TypeError(
                f"Malformed Event - could not parse!! Event: {str(event)}"))
        logger.exception(
            f"Malformed Event - could not parse!! Event: {str(event)}")
        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=DEFAULT_RULES.keys(),
        event=event.get("event"),
        template_url=event.get("stack_template_url", "N/A"),
        aws_region=event.get("region", "N/A"),
        aws_account_name=event.get("account", {}).get("name", "N/A"),
        aws_account_id=event.get("account", {}).get("id", "N/A"),
        aws_user_agent=event.get("user_agent", "N/A"),
    )

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

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

    processor.process_cf_template(template, config, result)

    perform_logging(result, config, event)

    return {
        "valid":
        result.valid,
        "reason":
        ",".join([
            "{}-{}".format(r["rule"], r["reason"]) for r in result.failed_rules
        ]),
        "failed_rules":
        RuleProcessor.remove_debug_rules(rules=result.failed_rules),
        "exceptions": [x.args[0] for x in result.exceptions],
        "warnings":
        RuleProcessor.remove_debug_rules(rules=result.failed_monitored_rules),
    }
Пример #22
0
def test_remove_debug_rules_no_rules():
    processed_list = RuleProcessor.remove_debug_rules(rules=[])
    assert [] == processed_list
Пример #23
0
    def test_with_no_rules(self):
        processor = RuleProcessor()
        config = Mock()
        result = Mock()

        processor.process_cf_template(EXAMPLE_CF_TEMPLATE, config, result)
Пример #24
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,
    }
Пример #25
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,
        })
    }