# Buggy code
def submit():
    status_code = None
    response_body = None
    data = request.get_json()
    newhost = {}
    newhost = json.loads(data)
    newhost["ctime"] = dt.now(tz.utc)
    if newhost["scan_reason"] == "requested":
        mark_scan_completed(newhost["ip"], newhost["scan_id"])

    try:
        nmap = NmapParser.parse(newhost["xml_data"])
        # If there's more or less than 1 host in the xml data, reject it (for now)
        if nmap.hosts_total != 1:
            status_code = 400
            response_body = json.dumps(
                {
                    "status": status_code,
                    "message": "XML had too many hosts in it",
                    "retry": False,
                }
            )

        # If it's not an acceptable target, tell the agent it's out of scope
        elif not current_app.ScopeManager.isAcceptableTarget(nmap.hosts[0].address):
            status_code = 400
            response_body = json.dumps(
                {
                    "status": status_code,
                    "message": "Out of scope: " + nmap.hosts[0].address,
                    "retry": False,
                }
            )

        # If there's no further processing to do, store the host and prepare the response
        elif not newhost["is_up"] or (newhost["is_up"] and newhost["port_count"] == 0):
            current_app.elastic.newhost(newhost)
            status_code = 200
            response_body = json.dumps(
                {"status": status_code, "message": "Received: " + newhost["ip"]}
            )
    except NmapParserException:
        status_code = 400
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "Invalid nmap xml data provided",
                "retry": False,
            }
        )

    # If status_code and response_body have been set by this point, return a response.
    if status_code and response_body:
        response = Response(
            response=response_body, status=status_code, content_type=json_content
        )
        return response

    if newhost["scan_start"] and newhost["scan_stop"]:
        elapsed = dateutil.parser.parse(newhost["scan_stop"]) - dateutil.parser.parse(
            newhost["scan_start"]
        )
        newhost["elapsed"] = elapsed.seconds

    newhost["ip"] = nmap.hosts[0].address
    if len(nmap.hosts[0].hostnames) > 0:
        newhost["hostname"] = nmap.hosts[0].hostnames[0]

    tmpports = []
    newhost["ports"] = []

    for port in nmap.hosts[0].get_open_ports():
        tmpports.append(str(port[0]))
        srv = nmap.hosts[0].get_service(port[0], port[1])
        portinfo = srv.get_dict()
        portinfo["service"] = srv.service_dict
        portinfo["scripts"] = []
        for script in srv.scripts_results:
            scriptsave = {"id": script["id"], "output": script["output"]}
            portinfo["scripts"].append(scriptsave)
            if script["id"] == "ssl-cert":
                portinfo["ssl"] = parse_ssl_data(script)

        newhost["ports"].append(portinfo)

    newhost["port_str"] = ", ".join(tmpports)

    if "screenshots" in newhost and newhost["screenshots"]:
        newhost["screenshots"], newhost["num_screenshots"] = process_screenshots(
            newhost["screenshots"]
        )

    if len(newhost["ports"]) == 0:
        status_code = 200
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "Expected open ports but didn't find any for %s"
                % newhost["ip"],
            }
        )
    elif len(newhost["ports"]) > 500:
        status_code = 200
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "More than 500 ports found, throwing data out",
            }
        )
    else:
        status_code = 200
        current_app.elastic.newhost(newhost)
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "Received %s ports for %s"
                % (len(newhost["ports"]), newhost["ip"]),
            }
        )

    response = Response(
        response=response_body, status=status_code, content_type=json_content
    )
    return response


# Traceback (TypeError)
Nov 05 17:22:50 natlas-server bash[10026]: [2019-11-05 17:22:50,681] ERROR in app: Exception on /api/getwork [GET]
Nov 05 17:22:50 natlas-server bash[10026]: Traceback (most recent call last):
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
Nov 05 17:22:50 natlas-server bash[10026]:     response = self.full_dispatch_request()
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
Nov 05 17:22:50 natlas-server bash[10026]:     rv = self.handle_user_exception(e)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
Nov 05 17:22:50 natlas-server bash[10026]:     reraise(exc_type, exc_value, tb)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
Nov 05 17:22:50 natlas-server bash[10026]:     raise value
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
Nov 05 17:22:50 natlas-server bash[10026]:     rv = self.dispatch_request()
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
Nov 05 17:22:50 natlas-server bash[10026]:     return self.view_functions[rule.endpoint](**req.view_args)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/auth/wrappers.py", line 41, in decorated_function
Nov 05 17:22:50 natlas-server bash[10026]:     return f(*args, **kwargs)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/api/routes.py", line 70, in getwork
Nov 05 17:22:50 natlas-server bash[10026]:     count, context = current_app.elastic.gethost_scan_id(rand)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/elastic.py", line 238, in gethost_scan_id
Nov 05 17:22:50 natlas-server bash[10026]:     if not self.checkStatus():
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/elastic.py", line 60, in checkStatus
Nov 05 17:22:50 natlas-server bash[10026]:     if not self.attemptReconnect():
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/elastic.py", line 43, in attemptReconnect
Nov 05 17:22:50 natlas-server bash[10026]:     delta = now - self.lastReconnectAttempt
Nov 05 17:22:50 natlas-server bash[10026]: TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'NoneType

# Fixed code
def submit():
    status_code = None
    response_body = None
    data = request.get_json()
    newhost = {}
    newhost = json.loads(data)
    newhost["ctime"] = dt.now(tz.utc)
    if newhost["scan_reason"] == "requested":
        mark_scan_completed(newhost["ip"], newhost["scan_id"])

    try:
        nmap = NmapParser.parse(newhost["xml_data"])
        # If there's more or less than 1 host in the xml data, reject it (for now)
        if nmap.hosts_total != 1:
            status_code = 400
            response_body = json.dumps(
                {
                    "status": status_code,
                    "message": "XML had too many hosts in it",
                    "retry": False,
                }
            )

        # If it's not an acceptable target, tell the agent it's out of scope
        elif not current_app.ScopeManager.isAcceptableTarget(nmap.hosts[0].address):
            status_code = 400
            response_body = json.dumps(
                {
                    "status": status_code,
                    "message": "Out of scope: " + nmap.hosts[0].address,
                    "retry": False,
                }
            )

        # If there's no further processing to do, store the host and prepare the response
        elif not newhost["is_up"] or (newhost["is_up"] and newhost["port_count"] == 0):
            current_app.elastic.new_result(newhost)
            status_code = 200
            response_body = json.dumps(
                {"status": status_code, "message": "Received: " + newhost["ip"]}
            )
    except NmapParserException:
        status_code = 400
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "Invalid nmap xml data provided",
                "retry": False,
            }
        )

    # If status_code and response_body have been set by this point, return a response.
    if status_code and response_body:
        response = Response(
            response=response_body, status=status_code, content_type=json_content
        )
        return response

    if newhost["scan_start"] and newhost["scan_stop"]:
        elapsed = dateutil.parser.parse(newhost["scan_stop"]) - dateutil.parser.parse(
            newhost["scan_start"]
        )
        newhost["elapsed"] = elapsed.seconds

    newhost["ip"] = nmap.hosts[0].address
    if len(nmap.hosts[0].hostnames) > 0:
        newhost["hostname"] = nmap.hosts[0].hostnames[0]

    tmpports = []
    newhost["ports"] = []

    for port in nmap.hosts[0].get_open_ports():
        tmpports.append(str(port[0]))
        srv = nmap.hosts[0].get_service(port[0], port[1])
        portinfo = srv.get_dict()
        portinfo["service"] = srv.service_dict
        portinfo["scripts"] = []
        for script in srv.scripts_results:
            scriptsave = {"id": script["id"], "output": script["output"]}
            portinfo["scripts"].append(scriptsave)
            if script["id"] == "ssl-cert":
                portinfo["ssl"] = parse_ssl_data(script)

        newhost["ports"].append(portinfo)

    newhost["port_str"] = ", ".join(tmpports)

    if "screenshots" in newhost and newhost["screenshots"]:
        newhost["screenshots"], newhost["num_screenshots"] = process_screenshots(
            newhost["screenshots"]
        )

    if len(newhost["ports"]) == 0:
        status_code = 200
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "Expected open ports but didn't find any for %s"
                % newhost["ip"],
            }
        )
    elif len(newhost["ports"]) > 500:
        status_code = 200
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "More than 500 ports found, throwing data out",
            }
        )
    else:
        status_code = 200
        current_app.elastic.new_result(newhost)
        response_body = json.dumps(
            {
                "status": status_code,
                "message": "Received %s ports for %s"
                % (len(newhost["ports"]), newhost["ip"]),
            }
        )

    response = Response(
        response=response_body, status=status_code, content_type=json_content
    )
    return response