From d8b726e1ba38849c44a6a6acb38b7809bb8299dd Mon Sep 17 00:00:00 2001 From: Taylor Date: Wed, 11 Sep 2024 12:07:11 -0500 Subject: [PATCH 01/11] scoutesuite gcp --- .../scoutsuite/routes/scoutsuite.py | 46 +++++++++++++++++-- .../scoutsuite/schema/scoutsuite.py | 3 ++ .../scoutsuite/services/scoutsuite.py | 30 +++++++++++- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/backend/app/integrations/scoutsuite/routes/scoutsuite.py b/backend/app/integrations/scoutsuite/routes/scoutsuite.py index a59834a16..40145167d 100644 --- a/backend/app/integrations/scoutsuite/routes/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/routes/scoutsuite.py @@ -1,22 +1,22 @@ import os - +import aiofiles from fastapi import APIRouter from fastapi import BackgroundTasks -from fastapi import HTTPException +from fastapi import HTTPException, UploadFile, File from loguru import logger from app.integrations.scoutsuite.schema.scoutsuite import ( AvailableScoutSuiteReportsResponse, ) from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest -from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest +from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportOptions from app.integrations.scoutsuite.schema.scoutsuite import ( ScoutSuiteReportOptionsResponse, ) from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportResponse from app.integrations.scoutsuite.services.scoutsuite import ( - generate_aws_report_background, + generate_aws_report_background, generate_gcp_report_background ) from app.integrations.scoutsuite.services.scoutsuite import ( generate_azure_report_background, @@ -121,6 +121,44 @@ async def generate_azure_report( message="Azure ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.", ) +@integration_scoutsuite_router.post( + "/generate-gcp-report", + response_model=ScoutSuiteReportResponse, +) +async def generate_gcp_report( + background_tasks: BackgroundTasks, + file: UploadFile = File(...), + report_name: str = "gcp-report", +): + """ + Endpoint to generate a GCP ScoutSuite report. + + Args: + background_tasks (BackgroundTasks): The background tasks object. + file (UploadFile): The uploaded JSON file. + """ + try: + # Ensure the scoutsuite directory exists + directory = os.path.join(os.getcwd(), "scoutsuite-report") + os.makedirs(directory, exist_ok=True) + + # Write the uploaded file to the scoutsuite directory + file_path = os.path.join(directory, file.filename) + async with aiofiles.open(file_path, 'wb') as out_file: + contents = await file.read() + await out_file.write(contents) + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + + logger.info(f"File saved to: {file_path}") + request = GCPScoutSuiteReportRequest(report_name=report_name, file_path=file_path) + logger.info(f"Request: {request}") + background_tasks.add_task(generate_gcp_report_background, request) + return ScoutSuiteReportResponse( + success=True, + message="GCP ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.", + ) + @integration_scoutsuite_router.delete( "/delete-report/{report_name}", diff --git a/backend/app/integrations/scoutsuite/schema/scoutsuite.py b/backend/app/integrations/scoutsuite/schema/scoutsuite.py index 2a366ee0f..44291036f 100644 --- a/backend/app/integrations/scoutsuite/schema/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/schema/scoutsuite.py @@ -51,6 +51,9 @@ def validate_report_type(cls, values): raise HTTPException(status_code=400, detail="Invalid report type.") return values +class GCPScoutSuiteReportRequest(BaseModel): + report_name: str = Field(..., description="The name of the report", example="aws-report") + file_path: str = Field(..., description="The path to the GCP credentials file", example="gcp-credentials.json") class ScoutSuiteReportResponse(BaseModel): success: bool diff --git a/backend/app/integrations/scoutsuite/services/scoutsuite.py b/backend/app/integrations/scoutsuite/services/scoutsuite.py index bca316cf1..3c8f1738b 100644 --- a/backend/app/integrations/scoutsuite/services/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/services/scoutsuite.py @@ -1,11 +1,12 @@ import asyncio import subprocess from concurrent.futures import ThreadPoolExecutor +import os from loguru import logger from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest -from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest +from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest async def generate_aws_report_background(request: AWSScoutSuiteReportRequest): @@ -56,6 +57,33 @@ def construct_azure_command(request: AzureScoutSuiteReportRequest): "--no-browser", ] +async def generate_gcp_report_background(request: GCPScoutSuiteReportRequest): + logger.info("Generating GCP ScoutSuite report in the background") + + command = construct_gcp_command(request) + await run_command_in_background(command) + + # Delete the file after the report is generated + try: + os.remove(request.file_path) + logger.info(f"Deleted GCP credentials file: {request.file_path}") + except Exception as e: + logger.error(f"Error deleting GCP credentials file: {e}") + +def construct_gcp_command(request: GCPScoutSuiteReportRequest): + """Construct the scout command.""" + return [ + "scout", + "gcp", + "--service-account", + request.file_path, + "--report-name", + request.report_name, + "--force", + "--no-browser", + ] + + def run_command(command): """Run the command and handle the output.""" From 41fbc35286ab09efd079a8efeda450fab2933d78 Mon Sep 17 00:00:00 2001 From: Taylor Date: Wed, 11 Sep 2024 15:21:30 -0500 Subject: [PATCH 02/11] bitdefender markdown fix --- .github/workflows/docker.yml | 26 +++++++++---------- .../app/integrations/markdown/bitdefender.md | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7003bdeae..3c127037c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,7 +2,7 @@ name: Docker on: push: - branches: [main] + branches: [scoutsuite-gcp] jobs: build-backend: @@ -31,12 +31,12 @@ jobs: build-args: | COPILOT_API_KEY=${{ secrets.COPILOT_API_KEY }} - - name: Notify Discord - uses: appleboy/discord-action@v1.0.0 - with: - webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - message: "Docker image for backend has been updated." + # - name: Notify Discord + # uses: appleboy/discord-action@v1.0.0 + # with: + # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + # message: "Docker image for backend has been updated." build-frontend: runs-on: ubuntu-latest @@ -62,9 +62,9 @@ jobs: push: true tags: ghcr.io/socfortress/copilot-frontend:latest - - name: Notify Discord - uses: appleboy/discord-action@v1.0.0 - with: - webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - message: "Docker image for frontend has been updated." + # - name: Notify Discord + # uses: appleboy/discord-action@v1.0.0 + # with: + # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + # message: "Docker image for frontend has been updated." diff --git a/backend/app/integrations/markdown/bitdefender.md b/backend/app/integrations/markdown/bitdefender.md index 2b2ff861f..1dfe620ed 100644 --- a/backend/app/integrations/markdown/bitdefender.md +++ b/backend/app/integrations/markdown/bitdefender.md @@ -13,7 +13,7 @@ The connector uses the POST method to receive authenticated and secured messages What we will be deploying is an HTTP endpoint that will receive the BitDefender logs and forward them to a syslog server. The HTTP endpoint is running on the server running CoPilot and will be listening on the port that you will define. This means that we must configure public DNS records to point to your edge firewall and open the port that you will define in the firewall. Traffic flow will be as follows: -BitDefender Cloud Platform -> Your Edge Firewall -> CoPilot Server -> Graylog Server +BitDefender Cloud Platform _ Your Edge Firewall _ CoPilot Server _ Graylog Server ## Configuration From c45f1ec05f46c48f185cbaa8da74512546686027 Mon Sep 17 00:00:00 2001 From: Taylor Date: Wed, 11 Sep 2024 15:24:28 -0500 Subject: [PATCH 03/11] update back to main --- .github/workflows/docker.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3c127037c..7003bdeae 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,7 +2,7 @@ name: Docker on: push: - branches: [scoutsuite-gcp] + branches: [main] jobs: build-backend: @@ -31,12 +31,12 @@ jobs: build-args: | COPILOT_API_KEY=${{ secrets.COPILOT_API_KEY }} - # - name: Notify Discord - # uses: appleboy/discord-action@v1.0.0 - # with: - # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - # message: "Docker image for backend has been updated." + - name: Notify Discord + uses: appleboy/discord-action@v1.0.0 + with: + webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + message: "Docker image for backend has been updated." build-frontend: runs-on: ubuntu-latest @@ -62,9 +62,9 @@ jobs: push: true tags: ghcr.io/socfortress/copilot-frontend:latest - # - name: Notify Discord - # uses: appleboy/discord-action@v1.0.0 - # with: - # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - # message: "Docker image for frontend has been updated." + - name: Notify Discord + uses: appleboy/discord-action@v1.0.0 + with: + webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + message: "Docker image for frontend has been updated." From 76469eed0eb6bd28bd4a362a678c21919a3d3039 Mon Sep 17 00:00:00 2001 From: Taylor Date: Wed, 11 Sep 2024 16:35:30 -0500 Subject: [PATCH 04/11] remove bash commands from markdown was causing DB to crash --- .github/workflows/docker.yml | 26 +++++------ .../app/integrations/markdown/bitdefender.md | 43 +------------------ 2 files changed, 14 insertions(+), 55 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7003bdeae..3c127037c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,7 +2,7 @@ name: Docker on: push: - branches: [main] + branches: [scoutsuite-gcp] jobs: build-backend: @@ -31,12 +31,12 @@ jobs: build-args: | COPILOT_API_KEY=${{ secrets.COPILOT_API_KEY }} - - name: Notify Discord - uses: appleboy/discord-action@v1.0.0 - with: - webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - message: "Docker image for backend has been updated." + # - name: Notify Discord + # uses: appleboy/discord-action@v1.0.0 + # with: + # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + # message: "Docker image for backend has been updated." build-frontend: runs-on: ubuntu-latest @@ -62,9 +62,9 @@ jobs: push: true tags: ghcr.io/socfortress/copilot-frontend:latest - - name: Notify Discord - uses: appleboy/discord-action@v1.0.0 - with: - webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - message: "Docker image for frontend has been updated." + # - name: Notify Discord + # uses: appleboy/discord-action@v1.0.0 + # with: + # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + # message: "Docker image for frontend has been updated." diff --git a/backend/app/integrations/markdown/bitdefender.md b/backend/app/integrations/markdown/bitdefender.md index 1dfe620ed..6a5106de9 100644 --- a/backend/app/integrations/markdown/bitdefender.md +++ b/backend/app/integrations/markdown/bitdefender.md @@ -59,45 +59,4 @@ You should now see the container running. ## Test the Connector -[Helpful Doc](https://support.netenrich.com/hc/en-us/articles/10833633251869-Bitdefender-Gravity-Zone-Cloud-integration#:~:text=155.173,Configure%20Chronicle%20Forwarder) - -Use the following cURL command to send the test payload to the collector service you have just configured: - -Replace `YOUR_AUTH_HEADER` with the base64 (https://www.blitter.se/utils/basic-authentication-header-generator/) encoded string of `username:password` and `REPLACE_WITH_YOUR_WEBSERVER` with the public DNS name you configured. - -### NOTE: This only tests that your endpoint is reachable and that the logs are being sent to the endpoint. You will need to verify that the logs are being sent to the Graylog server. - -```bash -curl -k -H 'Authorization: Basic YOUR_AUTH_HEADER' -H "Content-Type: application/json" -d -'{"cef": "0","events": -["CEF:0|Bitdefender|GravityZone|6.4.08|70000|Registration|3|BitdefenderGZModule=registrationd -vchost=TEST_ENDPOINTasdadBitdefenderGZComputerFQDN=test.example.com -dvc=192.168.1.2","CEF:0|Bitdefender|GravityZone|6.4.0-8|35| -Product ModulesStatus|5|BitdefenderGZModule=modules -dvchost=TEST_ENDPOINTasdadBitdefenderGZComputerFQDN=test.example.com -dvc=192.168.1.2","CEF:0|Bitdefender|GravityZone|6.4.0-8|35| -Product ModulesStatus|5|BitdefenderGZModule=modules -dvchost=TEST_ENDPOINTasdadBitdefenderGZComputerFQDN=test.example.com dvc=192.168.1.2"]}' -https://REPLACE_WITH_YOUR_WEBSERVER:3200/api -``` - -Now that the HTTPS collector service is running and listening for messages, we can test the service by sending a test message to the BitDefender service. Use the following cURL command to send the test payload to the collector service you have just configured: - -Replace `YOUR_BITDEFENDER_API_KEY` with the BitDefender API key with the base64 encoded string of `API_KEY` followed by a colon `:`. For example, if the API key is `test`, the value I would base64 encode would be `test:`. Replace `REPLACE_WITH_YOUR_WEBSERVER` with the public DNS name you configured. - -```bash -$ curl --tlsv1.2 -sS -k -X POST \ -https://cloud.gravityzone.bitdefender.com/api/v1.0/jsonrpc/push \ --H 'authorization: Basic YOUR_BITDEFENDER_API_KEY' \ --H 'cache-control: no-cache' \ --H 'content-type: application/json' \ --d '{"id":"1","jsonrpc":"2.0","method":"setPushEventSettings", -"params":{"serviceSettings":{"requireValidSslCertificate":false,"authorization":"Basic -dGVzdDp0ZXN0","url":"https://REPLACE_WITH_YOUR_WEBSERVER:3200/api"},"serviceType":"jsonRPC","status":1, -"subscribeToEventTypes":{"adcloudgz":true,"antiexploit":true,"aph":true,"av":true,"avc":true,"dp":true, -"endpoint-moved-in":true,"endpoint-moved-out":true,"exchange-malware":true, -"exchange-user-credentials":true,"fw":true,"hd":true,"hwid-change":true,"install":true,"modules":true, -"network-monitor":true,"network-sandboxing":true,"new-incident":true,"ransomware-mitigation":true, -"registration":true,"supa-update-status":true,"sva":true,"sva-load":true,"task-status":true, -"troubleshooting-activity":true,"uc":true,"uninstall":true}}}' -``` +[Helpful Doc For Testing](https://support.netenrich.com/hc/en-us/articles/10833633251869-Bitdefender-Gravity-Zone-Cloud-integration#:~:text=155.173,Configure%20Chronicle%20Forwarder) From 79100e225e305d6743b0b9023c0534672341f4b7 Mon Sep 17 00:00:00 2001 From: Taylor Date: Wed, 11 Sep 2024 16:50:58 -0500 Subject: [PATCH 05/11] set back to main --- .github/workflows/docker.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3c127037c..7003bdeae 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,7 +2,7 @@ name: Docker on: push: - branches: [scoutsuite-gcp] + branches: [main] jobs: build-backend: @@ -31,12 +31,12 @@ jobs: build-args: | COPILOT_API_KEY=${{ secrets.COPILOT_API_KEY }} - # - name: Notify Discord - # uses: appleboy/discord-action@v1.0.0 - # with: - # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - # message: "Docker image for backend has been updated." + - name: Notify Discord + uses: appleboy/discord-action@v1.0.0 + with: + webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + message: "Docker image for backend has been updated." build-frontend: runs-on: ubuntu-latest @@ -62,9 +62,9 @@ jobs: push: true tags: ghcr.io/socfortress/copilot-frontend:latest - # - name: Notify Discord - # uses: appleboy/discord-action@v1.0.0 - # with: - # webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} - # webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - # message: "Docker image for frontend has been updated." + - name: Notify Discord + uses: appleboy/discord-action@v1.0.0 + with: + webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }} + webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} + message: "Docker image for frontend has been updated." From d09ea9de2d2033fba140975dc80c2579feb4b849 Mon Sep 17 00:00:00 2001 From: Davide Di Modica Date: Thu, 12 Sep 2024 16:35:28 +0200 Subject: [PATCH 06/11] chore: update vue dependencies --- frontend/package-lock.json | 539 +++---------------------------------- frontend/package.json | 16 +- 2 files changed, 50 insertions(+), 505 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 79d55f6cd..ee9510d57 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,9 +10,9 @@ "dependencies": { "@ajoelp/json-to-formdata": "^1.5.0", "@f3ve/vue-markdown-it": "^0.2.2", - "@fontsource/jetbrains-mono": "^5.0.22", - "@fontsource/lexend": "^5.0.22", - "@fontsource/public-sans": "^5.0.18", + "@fontsource/jetbrains-mono": "^5.1.0", + "@fontsource/lexend": "^5.1.0", + "@fontsource/public-sans": "^5.1.0", "@popperjs/core": "^2.11.8", "@shikijs/markdown-it": "^1.17.0", "@vueuse/components": "^11.0.3", @@ -41,7 +41,7 @@ "vue": "^3.5.4", "vue-advanced-cropper": "^2.8.9", "vue-highlight-words": "^3.0.1", - "vue-i18n": "^10.0.0", + "vue-i18n": "^10.0.1", "vue-router": "^4.4.4", "vue-sjv": "^0.0.6", "vue3-apexcharts": "^1.6.0", @@ -83,8 +83,8 @@ "sass": "^1.78.0", "start-server-and-test": "^2.0.7", "tailwind-config-viewer": "^2.0.4", - "tailwindcss": "^3.4.10", - "taze": "^0.16.7", + "tailwindcss": "^3.4.11", + "taze": "^0.16.8", "type-fest": "^4.26.1", "unplugin-vue-components": "^0.27.4", "vite": "^5.4.4", @@ -99,7 +99,7 @@ "node": ">=18.0.0" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.21.2" + "@rollup/rollup-linux-x64-gnu": "^4.21.3" } }, "node_modules/@ajoelp/json-to-formdata": { @@ -1413,19 +1413,19 @@ } }, "node_modules/@fontsource/jetbrains-mono": { - "version": "5.0.22", - "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.0.22.tgz", - "integrity": "sha512-eFjS3ilq9n9FeRCdSSDhj3BTtr74FYeJswSJr9VDIxb02A07sQ59VugLuq1EKs6eLha+IOT5+4qVpllGaQNNHg==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.1.0.tgz", + "integrity": "sha512-YzWpGYY3PoqvmvwW2Hb4NwkN5C7LO0FmZugEGcFe6cNNE/cMgH9JhW5LI6hjG+1+nwgSHyJtKmblHNrUqlyhWg==" }, "node_modules/@fontsource/lexend": { - "version": "5.0.22", - "resolved": "https://registry.npmjs.org/@fontsource/lexend/-/lexend-5.0.22.tgz", - "integrity": "sha512-Eb1T9KamX8rK4dCl3t/rL9a0uFp2/L477HO0I11RIZWdrDR3iQ4vx6L5hd866VrzH6H0TGPLfuW+jstbZeFeWA==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fontsource/lexend/-/lexend-5.1.0.tgz", + "integrity": "sha512-mqwRfsRobLoj4iHqkvMGMMiTYU7n0hbgGftGBKFGKGyedseQOQ3k7iCBVTAU9wIyfq02Rypd9BrLXChHwFvg0Q==" }, "node_modules/@fontsource/public-sans": { - "version": "5.0.18", - "resolved": "https://registry.npmjs.org/@fontsource/public-sans/-/public-sans-5.0.18.tgz", - "integrity": "sha512-dYx/ULF7pRkjBO1ncCwIRXWWmI2oCsMsWxheHgjtNANm4+prtfq3gdGy3KOsuNcAhEKn6BQeIyg0hFqeoTpAaQ==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fontsource/public-sans/-/public-sans-5.1.0.tgz", + "integrity": "sha512-94+468XL9dUxsZ5edw/AtBHLdIwkvqr3WBUMBD30LUQzJpHw/Fxd7VrgkXSIKRIwYi3pTuQ1Q2Vj3COGIwi58Q==" }, "node_modules/@hapi/hoek": { "version": "9.3.0", @@ -1488,12 +1488,12 @@ } }, "node_modules/@intlify/core-base": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.0.tgz", - "integrity": "sha512-o4d4Nve7YzU1YMR5VMqgPr8jDGTgT2pOOUtZa3JwCAhFnm40JYxfHdWToT7OEx6oJCBs/Q8HosJOhsimlF0C0Q==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.1.tgz", + "integrity": "sha512-6kpRGjhos95ph7QmEtP4tnWFTW102s71CLQAQwfsIGqOAcoJhzcYFpzIQ0gKXzqAIXsMD/hwM5qJ4ewqMHw3gg==", "dependencies": { - "@intlify/message-compiler": "10.0.0", - "@intlify/shared": "10.0.0" + "@intlify/message-compiler": "10.0.1", + "@intlify/shared": "10.0.1" }, "engines": { "node": ">= 16" @@ -1503,11 +1503,11 @@ } }, "node_modules/@intlify/message-compiler": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.0.tgz", - "integrity": "sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.1.tgz", + "integrity": "sha512-fPeykrcgVT5eOIlshTHiPCN8FV3AZyBOdMS3XaXzfQ6eL5wqfc29I/EdIv5YXVW5X8e/BgYeWjBC0Cuznsl/2g==", "dependencies": { - "@intlify/shared": "10.0.0", + "@intlify/shared": "10.0.1", "source-map-js": "^1.0.2" }, "engines": { @@ -1518,9 +1518,9 @@ } }, "node_modules/@intlify/shared": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.0.tgz", - "integrity": "sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.1.tgz", + "integrity": "sha512-b4h7IWdZl710DnAhET8lgfgZ4Y9A2IZx/gbli3Ec/zHtYCoPqLHmiM7kUNBrSZj7d/SSjcMMZHuz5I09x3PYZw==", "engines": { "node": ">= 16" }, @@ -1691,49 +1691,6 @@ "node": ">= 8" } }, - "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/redact": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", - "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", - "dev": true, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/@nuxt/kit": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.13.1.tgz", @@ -2034,9 +1991,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", - "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz", + "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==", "cpu": [ "x64" ], @@ -4072,35 +4029,6 @@ "node": ">=8" } }, - "node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, "node_modules/cache-content-type": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", @@ -5356,16 +5284,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -5399,12 +5317,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, "node_modules/error-stack-parser-es": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-0.1.5.tgz", @@ -6292,18 +6204,6 @@ "node": ">=14.14" } }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6692,24 +6592,6 @@ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" }, - "node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "dev": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -6805,12 +6687,6 @@ "node": ">= 0.6" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -7031,25 +6907,6 @@ "node": ">=10" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dev": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -7195,12 +7052,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7532,15 +7383,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -8133,29 +7975,6 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", - "dev": true, - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", @@ -8405,125 +8224,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -8730,40 +8430,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-package-arg": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", - "dev": true, - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", - "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", - "dev": true, - "dependencies": { - "@npmcli/redact": "^2.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^13.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/npm-run-all2": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.2.tgz", @@ -9677,15 +9343,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -9695,19 +9352,6 @@ "node": ">= 0.6.0" } }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/property-information": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", @@ -10127,15 +9771,6 @@ "node": ">=8" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -10546,44 +10181,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "dev": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/sortablejs": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", @@ -10636,12 +10233,6 @@ "node": "*" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - }, "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", @@ -10667,18 +10258,6 @@ "node": ">=0.10.0" } }, - "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -11135,9 +10714,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", - "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.11.tgz", + "integrity": "sha512-qhEuBcLemjSJk5ajccN9xJFtM/h0AVCPaA6C92jNP+M2J8kX+eMJHI7R2HFKUvvAsMpcfLILMCFYSeDwpMmlUg==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -11234,14 +10813,13 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/taze": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/taze/-/taze-0.16.7.tgz", - "integrity": "sha512-bVKeFJc/rewVI5MFcG8EK5+6jWx37c3IiDy9qnk9Pv6FV8OLu6GhTk1ru+KLmvGwQc2twqtKA8HW3HmjHA2bEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/taze/-/taze-0.16.8.tgz", + "integrity": "sha512-vrkKrpVxkLrvSJ5Hc0FNheaJGoxv+uxzMWbGJCpucUpN5CnUlfvdL+mK98LGVqQZSjaPcAbkSQ/08N520ddLaQ==", "dev": true, "dependencies": { "@antfu/ni": "^0.23.0", "js-yaml": "^4.1.0", - "npm-registry-fetch": "^17.1.0", "ofetch": "^1.3.4", "package-manager-detector": "^0.2.0", "tinyexec": "^0.3.0", @@ -11665,30 +11243,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/unist-util-is": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", @@ -11920,15 +11474,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/validator": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", @@ -12701,12 +12246,12 @@ } }, "node_modules/vue-i18n": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.0.tgz", - "integrity": "sha512-KxTfTEuZEGN5Bvgc9F49rgp94XyBFlSIszwF2SQlr3WoxOklySXdUuoVxIw5qPZthV0mJlGP8tjJR7loMJgKrQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.1.tgz", + "integrity": "sha512-SQVlSm/1S6AaG1wexvwq3ebXUrrkx75ZHD78UAs4/rYD/X3tsQxfm6ElpT4ZPegJQEgRtOJjGripqSrfqAENtg==", "dependencies": { - "@intlify/core-base": "10.0.0", - "@intlify/shared": "10.0.0", + "@intlify/core-base": "10.0.1", + "@intlify/shared": "10.0.1", "@vue/devtools-api": "^6.5.0" }, "engines": { diff --git a/frontend/package.json b/frontend/package.json index a9e455c03..28c64e3a3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,9 +32,9 @@ "dependencies": { "@ajoelp/json-to-formdata": "^1.5.0", "@f3ve/vue-markdown-it": "^0.2.2", - "@fontsource/jetbrains-mono": "^5.0.22", - "@fontsource/lexend": "^5.0.22", - "@fontsource/public-sans": "^5.0.18", + "@fontsource/jetbrains-mono": "^5.1.0", + "@fontsource/lexend": "^5.1.0", + "@fontsource/public-sans": "^5.1.0", "@popperjs/core": "^2.11.8", "@shikijs/markdown-it": "^1.17.0", "@vueuse/components": "^11.0.3", @@ -63,7 +63,7 @@ "vue": "^3.5.4", "vue-advanced-cropper": "^2.8.9", "vue-highlight-words": "^3.0.1", - "vue-i18n": "^10.0.0", + "vue-i18n": "^10.0.1", "vue-router": "^4.4.4", "vue-sjv": "^0.0.6", "vue3-apexcharts": "^1.6.0", @@ -105,8 +105,8 @@ "sass": "^1.78.0", "start-server-and-test": "^2.0.7", "tailwind-config-viewer": "^2.0.4", - "tailwindcss": "^3.4.10", - "taze": "^0.16.7", + "tailwindcss": "^3.4.11", + "taze": "^0.16.8", "type-fest": "^4.26.1", "unplugin-vue-components": "^0.27.4", "vite": "^5.4.4", @@ -118,7 +118,7 @@ "vue-tsc": "^2.1.6" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.21.2" + "@rollup/rollup-linux-x64-gnu": "^4.21.3" }, "overrides": { "@vue/eslint-config-typescript": { @@ -140,4 +140,4 @@ "engines": { "node": ">=18.0.0" } -} +} \ No newline at end of file From eebe33f97eaf177fe6e72fc5c1e06540b2927bdb Mon Sep 17 00:00:00 2001 From: Davide Di Modica Date: Thu, 12 Sep 2024 16:36:57 +0200 Subject: [PATCH 07/11] refactor: improve variable validation on sigma file upload --- .../src/components/sigma/actionsProviders/QueryUploadFile.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/sigma/actionsProviders/QueryUploadFile.vue b/frontend/src/components/sigma/actionsProviders/QueryUploadFile.vue index a99e8f697..1b0eb816e 100644 --- a/frontend/src/components/sigma/actionsProviders/QueryUploadFile.vue +++ b/frontend/src/components/sigma/actionsProviders/QueryUploadFile.vue @@ -48,7 +48,7 @@ const show = ref(false) const lastShow = ref(new Date().getTime()) const message = useMessage() const fileList = ref([]) -const yamlFile = computed(() => fileList.value?.[0].file || null) +const yamlFile = computed(() => fileList.value?.[0]?.file || null) const isValid = computed(() => fileList.value.length) function togglePopup() { From 6693cdb216cd6d8477e59605be7fe29947311d37 Mon Sep 17 00:00:00 2001 From: Davide Di Modica Date: Thu, 12 Sep 2024 16:39:36 +0200 Subject: [PATCH 08/11] feat: add new GCP form --- .../api/endpoints/cloudSecurityAssessment.ts | 11 +++ .../CreationReportForm.vue | 30 +++++-- .../FormTypes/GcpTypeForm.vue | 85 +++++++++++++++++++ .../src/types/cloudSecurityAssessment.d.ts | 6 +- 4 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/cloudSecurityAssessment/FormTypes/GcpTypeForm.vue diff --git a/frontend/src/api/endpoints/cloudSecurityAssessment.ts b/frontend/src/api/endpoints/cloudSecurityAssessment.ts index c1f98d2cb..d950740e8 100644 --- a/frontend/src/api/endpoints/cloudSecurityAssessment.ts +++ b/frontend/src/api/endpoints/cloudSecurityAssessment.ts @@ -3,6 +3,7 @@ import { HttpClient } from "../httpClient" import type { ScoutSuiteAwsReportPayload, ScoutSuiteAzureReportPayload, + ScoutSuiteGcpReportPayload, ScoutSuiteReport, ScoutSuiteReportPayload } from "@/types/cloudSecurityAssessment.d" @@ -25,6 +26,16 @@ export default { report_type: "azure" }) }, + generateGcpScoutSuiteReport(payload: ScoutSuiteReportPayload & ScoutSuiteGcpReportPayload) { + const form = new FormData() + form.append("file", new Blob([payload.file], { type: payload.file.type }), payload.file.name) + + return HttpClient.post(`/scoutsuite/generate-gcp-report`, form, { + params: { + report_name: payload.report_name + } + }) + }, deleteScoutSuiteReport(reportName: string) { return HttpClient.delete(`/scoutsuite/delete-report/${reportName}`) } diff --git a/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue b/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue index f674e1d60..0218ae6e3 100644 --- a/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue +++ b/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue @@ -35,6 +35,13 @@ @mounted="typeFormRef = $event" /> + +
Reset & { report_type: ScoutSuiteReportType | null } -type TypeFormPayload = ScoutSuiteAwsReportPayload | ScoutSuiteAzureReportPayload +type TypeFormPayload = Partial const emit = defineEmits<{ (e: "submitted"): void @@ -100,7 +109,7 @@ const typeFormValid = ref(false) const baseFormRef = ref(null) const typeFormRef = ref(null) -const availableTypes = ["aws", "azure"] +const availableTypes = ["aws", "azure", "gcp"] const reportTypeOptions = ref<{ label: string; value: string; disabled: boolean }[]>([]) @@ -207,6 +216,13 @@ function submit() { ...(typeForm.value as ScoutSuiteAzureReportPayload) }) break + case ScoutSuiteReportType.Gcp: + apiCall = Api.cloudSecurityAssessment.generateGcpScoutSuiteReport({ + ...baseForm.value, + report_type: ScoutSuiteReportType.Gcp, + ...(typeForm.value as ScoutSuiteGcpReportPayload) + }) + break } if (!apiCall) { @@ -218,9 +234,13 @@ function submit() { apiCall .then(res => { if (res.data.success) { - message.success(res.data?.message || `ScoutSuite report generation started successfully`, { - duration: 10 * 1000 - }) + message.success( + res.data?.message || + `ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.`, + { + duration: 10 * 1000 + } + ) emit("submitted") resetForm() } else { diff --git a/frontend/src/components/cloudSecurityAssessment/FormTypes/GcpTypeForm.vue b/frontend/src/components/cloudSecurityAssessment/FormTypes/GcpTypeForm.vue new file mode 100644 index 000000000..11958457c --- /dev/null +++ b/frontend/src/components/cloudSecurityAssessment/FormTypes/GcpTypeForm.vue @@ -0,0 +1,85 @@ + + + diff --git a/frontend/src/types/cloudSecurityAssessment.d.ts b/frontend/src/types/cloudSecurityAssessment.d.ts index 46d3c4249..4a2620da7 100644 --- a/frontend/src/types/cloudSecurityAssessment.d.ts +++ b/frontend/src/types/cloudSecurityAssessment.d.ts @@ -2,7 +2,8 @@ export type ScoutSuiteReport = string export enum ScoutSuiteReportType { AWS = "aws", - Azure = "azure" + Azure = "azure", + Gcp = "gcp" } export interface ScoutSuiteReportPayload { report_type: ScoutSuiteReportType @@ -17,3 +18,6 @@ export interface ScoutSuiteAzureReportPayload { password: string tenant_id: string } +export interface ScoutSuiteGcpReportPayload { + file: File +} From 9cb284936478cdb97606b46771e2cc7362ec1e89 Mon Sep 17 00:00:00 2001 From: Davide Di Modica Date: Thu, 12 Sep 2024 16:41:10 +0200 Subject: [PATCH 09/11] refactor: improve type definitions and style --- .../cloudSecurityAssessment/AvailableReportsList.vue | 2 +- .../cloudSecurityAssessment/FormTypes/AwsTypeForm.vue | 4 ++-- .../cloudSecurityAssessment/FormTypes/AzureTypeForm.vue | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue b/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue index bb3db5065..8c3e91449 100644 --- a/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue +++ b/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue @@ -50,7 +50,7 @@ v-model:show="showForm" display-directive="show" preset="card" - :style="{ maxWidth: 'min(600px, 90vw)', minHeight: 'min(300px, 90vh)', overflow: 'hidden' }" + :style="{ maxWidth: 'min(600px, 90vw)', minHeight: 'min(270px, 90vh)', overflow: 'hidden' }" title="Generate Report" :bordered="false" segmented diff --git a/frontend/src/components/cloudSecurityAssessment/FormTypes/AwsTypeForm.vue b/frontend/src/components/cloudSecurityAssessment/FormTypes/AwsTypeForm.vue index 5431dfc88..ef48fdfd5 100644 --- a/frontend/src/components/cloudSecurityAssessment/FormTypes/AwsTypeForm.vue +++ b/frontend/src/components/cloudSecurityAssessment/FormTypes/AwsTypeForm.vue @@ -24,11 +24,11 @@ import type { ScoutSuiteAwsReportPayload } from "@/types/cloudSecurityAssessment const emit = defineEmits<{ (e: "mounted", value: FormInst): void - (e: "model", value: ScoutSuiteAwsReportPayload): void + (e: "model", value: Partial): void (e: "valid", value: boolean): void }>() -const form = ref({ +const form = ref>({ access_key_id: "", secret_access_key: "" }) diff --git a/frontend/src/components/cloudSecurityAssessment/FormTypes/AzureTypeForm.vue b/frontend/src/components/cloudSecurityAssessment/FormTypes/AzureTypeForm.vue index 5bc401426..d2e112b61 100644 --- a/frontend/src/components/cloudSecurityAssessment/FormTypes/AzureTypeForm.vue +++ b/frontend/src/components/cloudSecurityAssessment/FormTypes/AzureTypeForm.vue @@ -29,11 +29,11 @@ import type { ScoutSuiteAzureReportPayload } from "@/types/cloudSecurityAssessme const emit = defineEmits<{ (e: "mounted", value: FormInst): void - (e: "model", value: ScoutSuiteAzureReportPayload): void + (e: "model", value: Partial): void (e: "valid", value: boolean): void }>() -const form = ref({ +const form = ref>({ username: "", password: "", tenant_id: "" From b400a26546e070f298a51ebee2969ef055a45747 Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 12 Sep 2024 10:46:58 -0500 Subject: [PATCH 10/11] gcp more modular --- .../scoutsuite/routes/scoutsuite.py | 30 ++++++++-------- .../scoutsuite/schema/scoutsuite.py | 15 +++++++- .../scoutsuite/services/scoutsuite.py | 34 ++++++++++++++++++- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/backend/app/integrations/scoutsuite/routes/scoutsuite.py b/backend/app/integrations/scoutsuite/routes/scoutsuite.py index 40145167d..f467986cb 100644 --- a/backend/app/integrations/scoutsuite/routes/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/routes/scoutsuite.py @@ -2,6 +2,7 @@ import aiofiles from fastapi import APIRouter from fastapi import BackgroundTasks +import json from fastapi import HTTPException, UploadFile, File from loguru import logger @@ -9,14 +10,14 @@ AvailableScoutSuiteReportsResponse, ) from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest -from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest +from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest, GCPScoutSuiteJSON from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportOptions from app.integrations.scoutsuite.schema.scoutsuite import ( ScoutSuiteReportOptionsResponse, ) from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportResponse from app.integrations.scoutsuite.services.scoutsuite import ( - generate_aws_report_background, generate_gcp_report_background + generate_aws_report_background, generate_gcp_report_background, read_json_file, save_file_to_directory, validate_json_data ) from app.integrations.scoutsuite.services.scoutsuite import ( generate_azure_report_background, @@ -121,6 +122,7 @@ async def generate_azure_report( message="Azure ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.", ) + @integration_scoutsuite_router.post( "/generate-gcp-report", response_model=ScoutSuiteReportResponse, @@ -137,18 +139,16 @@ async def generate_gcp_report( background_tasks (BackgroundTasks): The background tasks object. file (UploadFile): The uploaded JSON file. """ - try: - # Ensure the scoutsuite directory exists - directory = os.path.join(os.getcwd(), "scoutsuite-report") - os.makedirs(directory, exist_ok=True) - - # Write the uploaded file to the scoutsuite directory - file_path = os.path.join(directory, file.filename) - async with aiofiles.open(file_path, 'wb') as out_file: - contents = await file.read() - await out_file.write(contents) - except Exception as e: - raise HTTPException(status_code=400, detail=str(e)) + # Read the file contents + contents = await file.read() + + # Read and validate the JSON file + data = await read_json_file(contents) + validate_json_data(data) + + # Save the file to the scoutsuite-report directory + directory = os.path.join(os.getcwd(), "scoutsuite-report") + file_path = await save_file_to_directory(contents, directory, file.filename) logger.info(f"File saved to: {file_path}") request = GCPScoutSuiteReportRequest(report_name=report_name, file_path=file_path) @@ -160,6 +160,8 @@ async def generate_gcp_report( ) + + @integration_scoutsuite_router.delete( "/delete-report/{report_name}", response_model=ScoutSuiteReportResponse, diff --git a/backend/app/integrations/scoutsuite/schema/scoutsuite.py b/backend/app/integrations/scoutsuite/schema/scoutsuite.py index 44291036f..ba08a8246 100644 --- a/backend/app/integrations/scoutsuite/schema/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/schema/scoutsuite.py @@ -52,9 +52,22 @@ def validate_report_type(cls, values): return values class GCPScoutSuiteReportRequest(BaseModel): - report_name: str = Field(..., description="The name of the report", example="aws-report") + report_name: str = Field(..., description="The name of the report", example="gcp-report") file_path: str = Field(..., description="The path to the GCP credentials file", example="gcp-credentials.json") +class GCPScoutSuiteJSON(BaseModel): + type: str + project_id: str + private_key_id: str + private_key: str + client_email: str + client_id: str + auth_uri: str + token_uri: str + auth_provider_x509_cert_url: str + client_x509_cert_url: str + universe_domain: str + class ScoutSuiteReportResponse(BaseModel): success: bool message: str diff --git a/backend/app/integrations/scoutsuite/services/scoutsuite.py b/backend/app/integrations/scoutsuite/services/scoutsuite.py index 3c8f1738b..f62330495 100644 --- a/backend/app/integrations/scoutsuite/services/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/services/scoutsuite.py @@ -2,11 +2,16 @@ import subprocess from concurrent.futures import ThreadPoolExecutor import os +import json +import aiofiles +from fastapi import HTTPException +from fastapi import UploadFile + from loguru import logger from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest -from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest +from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest, GCPScoutSuiteJSON async def generate_aws_report_background(request: AWSScoutSuiteReportRequest): @@ -103,3 +108,30 @@ async def run_command_in_background(command): with ThreadPoolExecutor() as executor: loop = asyncio.get_event_loop() await loop.run_in_executor(executor, lambda: run_command(command)) + + + +async def read_json_file(contents: bytes) -> dict: + """Read and parse the JSON file.""" + try: + return json.loads(contents) + except json.JSONDecodeError as e: + raise HTTPException(status_code=400, detail=f"Invalid JSON file - {str(e)}") + +def validate_json_data(data: dict): + """Validate the JSON data against the GCPScoutSuiteJSON model.""" + try: + GCPScoutSuiteJSON(**data) + except Exception as e: + raise HTTPException(status_code=400, detail=f"JSON file does not have the correct format and fields - {str(e)}") + +async def save_file_to_directory(contents: bytes, directory: str, filename: str) -> str: + """Save the uploaded file to the specified directory.""" + try: + os.makedirs(directory, exist_ok=True) + file_path = os.path.join(directory, filename) + async with aiofiles.open(file_path, 'wb') as out_file: + await out_file.write(contents) + return file_path + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) From 40e2800aea4a025d0de54cb9c3beb8b28e4b55cd Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 12 Sep 2024 10:52:25 -0500 Subject: [PATCH 11/11] precommit fixes --- .../app/integrations/markdown/bitdefender.md | 2 +- .../scoutsuite/routes/scoutsuite.py | 20 ++++++++++++------- .../scoutsuite/schema/scoutsuite.py | 3 +++ .../scoutsuite/services/scoutsuite.py | 20 ++++++++++--------- frontend/package.json | 2 +- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/backend/app/integrations/markdown/bitdefender.md b/backend/app/integrations/markdown/bitdefender.md index 6a5106de9..315b17053 100644 --- a/backend/app/integrations/markdown/bitdefender.md +++ b/backend/app/integrations/markdown/bitdefender.md @@ -13,7 +13,7 @@ The connector uses the POST method to receive authenticated and secured messages What we will be deploying is an HTTP endpoint that will receive the BitDefender logs and forward them to a syslog server. The HTTP endpoint is running on the server running CoPilot and will be listening on the port that you will define. This means that we must configure public DNS records to point to your edge firewall and open the port that you will define in the firewall. Traffic flow will be as follows: -BitDefender Cloud Platform _ Your Edge Firewall _ CoPilot Server _ Graylog Server +BitDefender Cloud Platform _ Your Edge Firewall _ CoPilot Server \_ Graylog Server ## Configuration diff --git a/backend/app/integrations/scoutsuite/routes/scoutsuite.py b/backend/app/integrations/scoutsuite/routes/scoutsuite.py index f467986cb..3e8d106f6 100644 --- a/backend/app/integrations/scoutsuite/routes/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/routes/scoutsuite.py @@ -1,27 +1,35 @@ import os -import aiofiles + from fastapi import APIRouter from fastapi import BackgroundTasks -import json -from fastapi import HTTPException, UploadFile, File +from fastapi import File +from fastapi import HTTPException +from fastapi import UploadFile from loguru import logger from app.integrations.scoutsuite.schema.scoutsuite import ( AvailableScoutSuiteReportsResponse, ) from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest -from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest, GCPScoutSuiteJSON +from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest +from app.integrations.scoutsuite.schema.scoutsuite import GCPScoutSuiteReportRequest from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportOptions from app.integrations.scoutsuite.schema.scoutsuite import ( ScoutSuiteReportOptionsResponse, ) from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportResponse from app.integrations.scoutsuite.services.scoutsuite import ( - generate_aws_report_background, generate_gcp_report_background, read_json_file, save_file_to_directory, validate_json_data + generate_aws_report_background, ) from app.integrations.scoutsuite.services.scoutsuite import ( generate_azure_report_background, ) +from app.integrations.scoutsuite.services.scoutsuite import ( + generate_gcp_report_background, +) +from app.integrations.scoutsuite.services.scoutsuite import read_json_file +from app.integrations.scoutsuite.services.scoutsuite import save_file_to_directory +from app.integrations.scoutsuite.services.scoutsuite import validate_json_data integration_scoutsuite_router = APIRouter() @@ -160,8 +168,6 @@ async def generate_gcp_report( ) - - @integration_scoutsuite_router.delete( "/delete-report/{report_name}", response_model=ScoutSuiteReportResponse, diff --git a/backend/app/integrations/scoutsuite/schema/scoutsuite.py b/backend/app/integrations/scoutsuite/schema/scoutsuite.py index ba08a8246..9a4738e32 100644 --- a/backend/app/integrations/scoutsuite/schema/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/schema/scoutsuite.py @@ -51,10 +51,12 @@ def validate_report_type(cls, values): raise HTTPException(status_code=400, detail="Invalid report type.") return values + class GCPScoutSuiteReportRequest(BaseModel): report_name: str = Field(..., description="The name of the report", example="gcp-report") file_path: str = Field(..., description="The path to the GCP credentials file", example="gcp-credentials.json") + class GCPScoutSuiteJSON(BaseModel): type: str project_id: str @@ -68,6 +70,7 @@ class GCPScoutSuiteJSON(BaseModel): client_x509_cert_url: str universe_domain: str + class ScoutSuiteReportResponse(BaseModel): success: bool message: str diff --git a/backend/app/integrations/scoutsuite/services/scoutsuite.py b/backend/app/integrations/scoutsuite/services/scoutsuite.py index f62330495..0fa26003e 100644 --- a/backend/app/integrations/scoutsuite/services/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/services/scoutsuite.py @@ -1,17 +1,17 @@ import asyncio +import json +import os import subprocess from concurrent.futures import ThreadPoolExecutor -import os -import json + import aiofiles from fastapi import HTTPException -from fastapi import UploadFile - - from loguru import logger from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest -from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest, GCPScoutSuiteReportRequest, GCPScoutSuiteJSON +from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest +from app.integrations.scoutsuite.schema.scoutsuite import GCPScoutSuiteJSON +from app.integrations.scoutsuite.schema.scoutsuite import GCPScoutSuiteReportRequest async def generate_aws_report_background(request: AWSScoutSuiteReportRequest): @@ -62,6 +62,7 @@ def construct_azure_command(request: AzureScoutSuiteReportRequest): "--no-browser", ] + async def generate_gcp_report_background(request: GCPScoutSuiteReportRequest): logger.info("Generating GCP ScoutSuite report in the background") @@ -75,6 +76,7 @@ async def generate_gcp_report_background(request: GCPScoutSuiteReportRequest): except Exception as e: logger.error(f"Error deleting GCP credentials file: {e}") + def construct_gcp_command(request: GCPScoutSuiteReportRequest): """Construct the scout command.""" return [ @@ -89,7 +91,6 @@ def construct_gcp_command(request: GCPScoutSuiteReportRequest): ] - def run_command(command): """Run the command and handle the output.""" process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -110,7 +111,6 @@ async def run_command_in_background(command): await loop.run_in_executor(executor, lambda: run_command(command)) - async def read_json_file(contents: bytes) -> dict: """Read and parse the JSON file.""" try: @@ -118,6 +118,7 @@ async def read_json_file(contents: bytes) -> dict: except json.JSONDecodeError as e: raise HTTPException(status_code=400, detail=f"Invalid JSON file - {str(e)}") + def validate_json_data(data: dict): """Validate the JSON data against the GCPScoutSuiteJSON model.""" try: @@ -125,12 +126,13 @@ def validate_json_data(data: dict): except Exception as e: raise HTTPException(status_code=400, detail=f"JSON file does not have the correct format and fields - {str(e)}") + async def save_file_to_directory(contents: bytes, directory: str, filename: str) -> str: """Save the uploaded file to the specified directory.""" try: os.makedirs(directory, exist_ok=True) file_path = os.path.join(directory, filename) - async with aiofiles.open(file_path, 'wb') as out_file: + async with aiofiles.open(file_path, "wb") as out_file: await out_file.write(contents) return file_path except Exception as e: diff --git a/frontend/package.json b/frontend/package.json index 28c64e3a3..b3ae5799d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -140,4 +140,4 @@ "engines": { "node": ">=18.0.0" } -} \ No newline at end of file +}