diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4fd7e734af2233457014892f5a1056e4b94b5f2c --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,17 @@ +version: '3.9' +services: + mongoservice: + image: mongo:5.0 + ports: + - 27017:27017 + iac-scan-runner: + image: xscanner/runner + expose: + - 27017 + ports: + - 8080:80 + depends_on: + - "mongoservice" + environment: + - MONGODB_CONNECTION_STRING=mongodb://mongoservice:27017/ + - SCAN_PERSISTENCE=enabled diff --git a/src/iac_scan_runner/api.py b/src/iac_scan_runner/api.py index 352ec07fba591be7f0d3ddf9a1dbdf5c34ba62a7..f30795e27968380bd293cbcbccc3081e3c69c660 100644 --- a/src/iac_scan_runner/api.py +++ b/src/iac_scan_runner/api.py @@ -167,10 +167,10 @@ async def post_scan(iac: UploadFile = File(..., description='IaC file (zip or ta return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=str(e)) @app.get("/results", summary="Retrieve particular scan result by given uuid", responses={200: {}, 400: {"model": str}}) -async def get_scan_result(uuid: str) -> JSONResponse: +async def get_scan_result(uuid: Optional[str]) -> JSONResponse: """ Retrieve a particular scan result (GET method) - :param uuid: Identifier of a scan record in database + :param uuid: Identifier of a saved scan record :return: JSONResponse object (with status code 200 or 400) """ try: @@ -188,36 +188,17 @@ async def get_scan_result(uuid: str) -> JSONResponse: async def delete_scan_result(uuid: str) -> JSONResponse: """ Delete a particular scan result (GET method) - :param uuid: Identifier of a scan record in database + :param uuid: Identifier of a saved scan record :return: JSONResponse object (with status code 200 or 400) """ try: results_persistence = ResultsPersistence() result = results_persistence.show_result(uuid) - if(not result==None): + if(not result == None): results_persistence.delete_result(uuid) return JSONResponse(status_code=status.HTTP_200_OK, content=f"Deleted scan result {uuid}") else: return JSONResponse(status_code=status.HTTP_200_OK, content=f"No such scan result {uuid}") except Exception as e: return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=str(e)) - - -@app.put("/persistence_enabler/{enable}", summary="Delete particular scan result by given uuid", responses={200: {}, 400: {"model": str}}) -async def persistence_enable(enable: str) -> JSONResponse: - """ - Delete a particular scan result (GET method) - :param uuid: Identifier of a scan record in database - :return: JSONResponse object (with status code 200 or 400) - """ - try: - if(enable == "disable"): - scan_runner.persistence_enabled = False - else: - scan_runner.persistence_enabled = True - - return JSONResponse(status_code=status.HTTP_200_OK, content=f"Persistence enable: {enable}") - - except Exception as e: - return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=str(e)) diff --git a/src/iac_scan_runner/results_persistence.py b/src/iac_scan_runner/results_persistence.py index d70c22e99a0b58fde75c084785972237fdd06898..3feb70dc5861fd8d80cd269b8494e98e41ce3b3d 100644 --- a/src/iac_scan_runner/results_persistence.py +++ b/src/iac_scan_runner/results_persistence.py @@ -6,21 +6,32 @@ from datetime import datetime import os class ResultsPersistence: def __init__(self): - """ Initialize new scan result database, collection and client """ - + self.connect_db() + + def connect_db(self): + """ + Initialize new scan result database, collection and client + """ + try: - connection_string = os.environ['MONGO_STRING'] - print(connection_string) - self.myclient = pymongo.MongoClient(connection_string) - self.mydb = self.myclient["scandb"] - self.mycol = self.mydb["results"] - self.connection_problem = False + connection_string = os.environ['MONGODB_CONNECTION_STRING'] + if(connection_string): + self.myclient = pymongo.MongoClient(connection_string) + self.mydb = self.myclient["scandb"] + self.mycol = self.mydb["results"] + self.connection_problem = False + + # TODO: Consider more specific exceptions except Exception as e: print("Scan result persistence not available") + print(str(e)) + self.myclient = None + self.mydb = None + self.mycol = None self.connection_problem = True @@ -31,27 +42,38 @@ class ResultsPersistence: """Inserts new scan result into database :param result: Dictionary holding the scan summary - """ - self.mycol.insert_one(self.parse_json(result)) - + """ + if(self.connection_problem == True): + self.connect_db() + if(self.mycol != None): + self.mycol.insert_one(self.parse_json(result)) + def show_result(self, uuid4: str) -> str: """Shows scan result with given id :param uuid4: Identifier of a scan result :return: String representing the scan result record """ - myquery = { "uuid": uuid4 } - mydoc = self.mycol.find(myquery) - for x in mydoc: - return str(x) + if(self.connection_problem==True): + self.connect_db() + + if(self.mycol != None): + myquery = { "uuid": uuid4 } + mydoc = self.mycol.find(myquery) + for x in mydoc: + return str(x) def delete_result(self, uuid4: str): """Deletes the scan result with given id from database :param uuid4: Identifier of a scan result which is about to be deleted """ - myquery = { "uuid": uuid4 } - mydoc = self.mycol.delete_one(myquery) + if(self.connection_problem==True): + self.connect_db() + + if(self.mycol != None): + myquery = { "uuid": uuid4 } + mydoc = self.mycol.delete_one(myquery) def show_all(self) -> str: @@ -59,11 +81,15 @@ class ResultsPersistence: """Shows all the scan records from the database :return: String of all database records concatenated """ - cursor = self.mycol.find({}) - output = "" - for doc in cursor: - output = output + str(doc) - return output + if(self.connection_problem==True): + self.connect_db() + + if(self.mycol != None): + cursor = self.mycol.find({}) + output = "" + for doc in cursor: + output = output + str(doc) + return output def days_passed(self, time_stamp: str) -> int: @@ -75,7 +101,8 @@ class ResultsPersistence: time2 = datetime.now() # current date and time delta = time2 - time1 string_delta = str(delta) - if(string_delta.find("days")>-1): + + if(string_delta.find("days") > -1): days = string_delta.split(" ") days = days[0] return int(days) @@ -89,22 +116,30 @@ class ResultsPersistence: :param uuid4: Identifier of a scan result :return: Integer denoting scan result age """ - myquery = { "uuid": uuid4 } - mydoc = self.mycol.find(myquery) - for x in mydoc: - scan_ts = x["time"] - - return self.days_passed(scan_ts) + if(self.connection_problem == True): + self.connect_db() + + if(self.mycol != None): + myquery = { "uuid": uuid4 } + mydoc = self.mycol.find(myquery) + for x in mydoc: + scan_ts = x["time"] + + return self.days_passed(scan_ts) def periodic_clean_job(self): """Calculates how long a scan result resides in database since its insertion :param uuid4: Identifier of a scan result """ - cursor = self.mycol.find({}) - scan_ts = "" - for doc in cursor: - doc_uuid = doc["uuid"] - age = self.result_age(doc_uuid) - if(age>14): - self.delete_result(doc_uuid) + if(self.connection_problem == True): + self.connect_db() + + if(self.mycol != None): + cursor = self.mycol.find({}) + scan_ts = "" + for doc in cursor: + doc_uuid = doc["uuid"] + age = self.result_age(doc_uuid) + if(age>14): + self.delete_result(doc_uuid) diff --git a/src/iac_scan_runner/scan_runner.py b/src/iac_scan_runner/scan_runner.py index 99494eeb16d152c7ddc25d445b7dad0151812163..f66d3595adc0aff3e02d75ab504b6b9976b9c146 100644 --- a/src/iac_scan_runner/scan_runner.py +++ b/src/iac_scan_runner/scan_runner.py @@ -55,7 +55,12 @@ class ScanRunner: self.compatibility_matrix = Compatibility() self.results_summary = ResultsSummary() self.archive_name = "" - self.persistence_enabled = True + + if(os.environ.get("SCAN_PERSISTENCE") == "enabled"): + self.persistence_enabled = True + else: + self.persistence_enabled = False + self.results_persistence = ResultsPersistence() def init_checks(self):