Exemplo n.º 1
0
 def process_cf_template(self,
                         cfmodel: CFModel,
                         config: Config,
                         extras: Optional[Dict] = None) -> Result:
     result = Result()
     for rule in self.rules:
         try:
             result += rule.invoke(cfmodel, extras)
         except Exception as other_exception:
             result.add_exception(other_exception)
             logger.exception(
                 "{} crashed with {} for project - {}, service - {}, stack - {}"
                 .format(
                     type(rule).__name__,
                     type(other_exception).__name__,
                     config.project_name,
                     config.service_name,
                     config.stack_name,
                 ))
             continue
     self.remove_failures_of_whitelisted_actions(config=config,
                                                 result=result)
     self.remove_failures_of_whitelisted_resources(config=config,
                                                   result=result)
     return result
Exemplo n.º 2
0
    def process_cf_template(self,
                            cfmodel: CFModel,
                            config: Config,
                            extras: Optional[Dict] = None) -> Result:
        result = Result()
        for rule in self.rules:
            if rule.rule_mode == RuleMode.DISABLED:
                continue

            try:
                result += rule.invoke(cfmodel, extras)
            except Exception as other_exception:
                result.add_exception(other_exception)
                logger.exception(
                    "{} crashed with {} for project - {}, service - {}, stack - {}"
                    .format(
                        type(rule).__name__,
                        type(other_exception).__name__,
                        config.project_name,
                        config.service_name,
                        config.stack_name,
                    ))
                continue
        return result
Exemplo n.º 3
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),
    }
Exemplo n.º 4
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,
        })
    }
Exemplo n.º 5
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,
    }