diff --git a/backend/app/integrations/markdown/bitdefender.md b/backend/app/integrations/markdown/bitdefender.md index 2b2ff861f..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 @@ -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) diff --git a/backend/app/integrations/scoutsuite/routes/scoutsuite.py b/backend/app/integrations/scoutsuite/routes/scoutsuite.py index a59834a16..3e8d106f6 100644 --- a/backend/app/integrations/scoutsuite/routes/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/routes/scoutsuite.py @@ -2,7 +2,9 @@ from fastapi import APIRouter from fastapi import BackgroundTasks +from fastapi import File from fastapi import HTTPException +from fastapi import UploadFile from loguru import logger from app.integrations.scoutsuite.schema.scoutsuite import ( @@ -10,6 +12,7 @@ ) from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest 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, @@ -21,6 +24,12 @@ 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() @@ -122,6 +131,43 @@ async def generate_azure_report( ) +@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. + """ + # 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) + 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}", response_model=ScoutSuiteReportResponse, diff --git a/backend/app/integrations/scoutsuite/schema/scoutsuite.py b/backend/app/integrations/scoutsuite/schema/scoutsuite.py index 2a366ee0f..9a4738e32 100644 --- a/backend/app/integrations/scoutsuite/schema/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/schema/scoutsuite.py @@ -52,6 +52,25 @@ def validate_report_type(cls, values): 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 + 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 bca316cf1..0fa26003e 100644 --- a/backend/app/integrations/scoutsuite/services/scoutsuite.py +++ b/backend/app/integrations/scoutsuite/services/scoutsuite.py @@ -1,11 +1,17 @@ import asyncio +import json +import os import subprocess from concurrent.futures import ThreadPoolExecutor +import aiofiles +from fastapi import HTTPException 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 GCPScoutSuiteJSON +from app.integrations.scoutsuite.schema.scoutsuite import GCPScoutSuiteReportRequest async def generate_aws_report_background(request: AWSScoutSuiteReportRequest): @@ -57,6 +63,34 @@ def construct_azure_command(request: AzureScoutSuiteReportRequest): ] +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.""" process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -75,3 +109,31 @@ 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)) 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..b3ae5799d 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": { 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/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/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/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: "" 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/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() { 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 +}