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
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
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), }
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, }) }
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, }