From 6f75c39bb5c05b98d8b6563db468e55a961dcd30 Mon Sep 17 00:00:00 2001 From: taylor_socfortress <111797488+taylorwalton@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:19:40 -0500 Subject: [PATCH] Scoutsuite (#234) * scoutsuite integration * refactor: Update AWS ScoutSuite report generation process * refactor: Update AWS ScoutSuite command construction * refactor: Update AWS ScoutSuite command construction * refactor: Update AWS ScoutSuite command construction * refactor: Delete ScoutSuite report and associated files * refactor: Update AWS ScoutSuite command construction * added cloud-security-assessment * updated overview page breakpoints * added cloudSecurityAssessment api/types * added AvailableReportsItem component * refactor: Remove unnecessary code in create_customer_provisioning_default_settings * refactor: modify admin password creation to fix bug with special characters * refactor: Update admin password generation to use longer length * bug: update ProvisioningDefaultSettings * provision ha proxy bug fix * refactor * update: AvailableReportsList component * refactor: Update available report generation options in ScoutSuite API to only include aws for now * added azure and gcp back to scoutsuite * add CreationReportForm component * fix: getBaseUrl function * precommit fixes --------- Co-authored-by: Davide Di Modica --- .gitignore | 1 + .vscode/settings.json | 2 + backend/app/auth/models/users.py | 4 +- backend/app/auth/services/universal.py | 2 +- .../routes/default_settings.py | 1 - .../services/provision.py | 3 +- .../scoutsuite/routes/scoutsuite.py | 122 +++++++++ .../scoutsuite/schema/scoutsuite.py | 48 ++++ .../scoutsuite/services/scoutsuite.py | 50 ++++ backend/app/routers/scoutsuite.py | 13 + backend/copilot.py | 10 + backend/requirements.txt | 1 + frontend/package-lock.json | 1 - frontend/package.json | 16 +- frontend/src/api/cloudSecurityAssessment.ts | 20 ++ frontend/src/api/customers.ts | 9 +- frontend/src/api/index.ts | 4 +- .../AvailableReportsItem.vue | 94 +++++++ .../AvailableReportsList.vue | 109 ++++++++ .../CloudSecurityAssessmentButton.vue | 26 ++ .../CreationReportForm.vue | 245 ++++++++++++++++++ .../src/components/customers/CustomerForm.vue | 2 +- .../components/customers/CustomerMetaForm.vue | 2 +- .../provision/CustomerDefaultSettingsForm.vue | 14 +- .../components/profile/ProfileSettings.vue | 7 +- frontend/src/router/index.ts | 6 + frontend/src/stores/settings.ts | 2 +- .../src/types/cloudSecurityAssessment.d.ts | 8 + frontend/src/types/customers.d.ts | 2 +- frontend/src/utils/index.ts | 5 + .../src/views/CloudSecurityAssessment.vue | 9 + frontend/src/views/Overview.vue | 13 +- 32 files changed, 811 insertions(+), 40 deletions(-) create mode 100644 backend/app/integrations/scoutsuite/routes/scoutsuite.py create mode 100644 backend/app/integrations/scoutsuite/schema/scoutsuite.py create mode 100644 backend/app/integrations/scoutsuite/services/scoutsuite.py create mode 100644 backend/app/routers/scoutsuite.py create mode 100644 frontend/src/api/cloudSecurityAssessment.ts create mode 100644 frontend/src/components/cloudSecurityAssessment/AvailableReportsItem.vue create mode 100644 frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue create mode 100644 frontend/src/components/cloudSecurityAssessment/CloudSecurityAssessmentButton.vue create mode 100644 frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue create mode 100644 frontend/src/types/cloudSecurityAssessment.d.ts create mode 100644 frontend/src/views/CloudSecurityAssessment.vue diff --git a/.gitignore b/.gitignore index ed112df3..b9997923 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ backend/data/api.config.yaml backend/file-store/api.config.yaml backend/report.pdf backend/report.html +backend/scoutsuite-report backend/app/integrations/office365/services/wazuh_config.xml frontend/src/unplugin.components.d.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 9193885e..c507f302 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "apexchart", "arcticons", "CARBONBLACK", + "clickoutside", "cmdline", "colord", "commonmark", @@ -37,6 +38,7 @@ "Popselect", "redoc", "rushstack", + "scoutsuite", "Shiki", "shikijs", "signin", diff --git a/backend/app/auth/models/users.py b/backend/app/auth/models/users.py index 1e4ce6ae..ad221654 100644 --- a/backend/app/auth/models/users.py +++ b/backend/app/auth/models/users.py @@ -118,20 +118,18 @@ def generate(cls, length: int = 12) -> "Password": lowercase = string.ascii_lowercase uppercase = string.ascii_uppercase digits = string.digits - punctuation = string.punctuation # Ensure the password has at least one lowercase, one uppercase, one digit, and one symbol password_chars = [ random.choice(lowercase), random.choice(uppercase), random.choice(digits), - random.choice(punctuation), ] # Fill the rest of the password length with a random mix of characters if length > 4: password_chars += random.choices( - lowercase + uppercase + digits + punctuation, + lowercase + uppercase + digits, k=length - 4, ) diff --git a/backend/app/auth/services/universal.py b/backend/app/auth/services/universal.py index 9142f3a9..bf099b5d 100644 --- a/backend/app/auth/services/universal.py +++ b/backend/app/auth/services/universal.py @@ -111,7 +111,7 @@ async def create_admin_user(session: AsyncSession): session, ): # The check function needs to be passed the session as well # Create the admin user - password_model = Password.generate(length=12) + password_model = Password.generate(length=24) admin_user = User( username="admin", password=password_model.hashed, # Assuming you store the hashed password diff --git a/backend/app/customer_provisioning/routes/default_settings.py b/backend/app/customer_provisioning/routes/default_settings.py index 537af47e..83abe130 100644 --- a/backend/app/customer_provisioning/routes/default_settings.py +++ b/backend/app/customer_provisioning/routes/default_settings.py @@ -64,7 +64,6 @@ async def create_customer_provisioning_default_settings( db.add(customer_provisioning_default_settings) await db.commit() - await db.refresh(customer_provisioning_default_settings) return CustomerProvisioningDefaultSettingsResponse( message="Customer Provisioning Default Settings created successfully", success=True, diff --git a/backend/app/customer_provisioning/services/provision.py b/backend/app/customer_provisioning/services/provision.py index 4bd386c1..3fea89cf 100644 --- a/backend/app/customer_provisioning/services/provision.py +++ b/backend/app/customer_provisioning/services/provision.py @@ -293,10 +293,11 @@ async def provision_haproxy( """ logger.info(f"Provisioning HAProxy {request}") api_endpoint = await get_connector_attribute( - connector_id=16, + connector_id=15, column_name="connector_url", session=session, ) + logger.info(f"HAProxy API endpoint: {api_endpoint}") # Send the POST request to the Wazuh worker response = requests.post( url=f"{api_endpoint}/provision_worker/haproxy", diff --git a/backend/app/integrations/scoutsuite/routes/scoutsuite.py b/backend/app/integrations/scoutsuite/routes/scoutsuite.py new file mode 100644 index 00000000..579b5e76 --- /dev/null +++ b/backend/app/integrations/scoutsuite/routes/scoutsuite.py @@ -0,0 +1,122 @@ +import os + +from fastapi import APIRouter +from fastapi import BackgroundTasks +from fastapi import HTTPException +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 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, +) + +integration_scoutsuite_router = APIRouter() + + +@integration_scoutsuite_router.get( + "/report-generation-options", + response_model=ScoutSuiteReportOptionsResponse, + description="Get the available report generation options.", +) +async def get_report_generation_options(): + """ + Retrieves the available report generation options for ScoutSuite. + + Returns: + ScoutSuiteReportOptionsResponse: The response containing the available report generation options. + """ + return ScoutSuiteReportOptionsResponse( + options=[ScoutSuiteReportOptions.aws, ScoutSuiteReportOptions.azure, ScoutSuiteReportOptions.gcp], + success=True, + message="ScoutSuite Report generation options retrieved successfully", + ) + + +@integration_scoutsuite_router.get( + "/available-reports", + response_model=AvailableScoutSuiteReportsResponse, + description="Get the available ScoutSuite reports.", +) +async def get_available_reports(): + """ + List all the `.html` files from the `scoutsuite-report` directory + + Returns: + AvailableScoutSuiteReportsResponse: The response containing the list of available ScoutSuite reports. + Raises: + HTTPException: If the directory does not exist. + """ + directory = "scoutsuite-report" + full_path = os.path.abspath(directory) + + logger.info(f"Checking directory: {full_path}") + + if not os.path.exists(directory): + raise HTTPException(status_code=404, detail="Directory does not exist") + + files = os.listdir(directory) + html_files = [file for file in files if file.endswith(".html")] + + return AvailableScoutSuiteReportsResponse( + available_reports=html_files, + success=True, + message="Available ScoutSuite reports retrieved successfully", + ) + + +@integration_scoutsuite_router.post( + "/generate-aws-report", + response_model=ScoutSuiteReportResponse, +) +async def generate_aws_report( + background_tasks: BackgroundTasks, + request: AWSScoutSuiteReportRequest, +): + """ + Endpoint to generate an AWS ScoutSuite report. + + Args: + background_tasks (BackgroundTasks): The background tasks object. + request (AWSScoutSuiteReportRequest): The request object. + session (AsyncSession): The async session object for database operations. + """ + background_tasks.add_task(generate_aws_report_background, request) + return ScoutSuiteReportResponse( + success=True, + message="AWS 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, +) +async def delete_report( + report_name: str, +): + """ + Endpoint to delete a ScoutSuite report. + + Args: + report_name (str): The name of the report to delete. + """ + report_base_name = os.path.splitext(report_name)[0] + report_file_path = f"scoutsuite-report/{report_name}" + exceptions_file_path = f"scoutsuite-report/scoutsuite-results/scoutsuite_exceptions_{report_base_name}.js" + results_file_path = f"scoutsuite-report/scoutsuite-results/scoutsuite_results_{report_base_name}.js" + + files_to_delete = [report_file_path, exceptions_file_path, results_file_path] + + for file_path in files_to_delete: + if os.path.exists(file_path): + os.remove(file_path) + + return ScoutSuiteReportResponse(success=True, message=f"Report {report_name} and associated files deleted successfully") diff --git a/backend/app/integrations/scoutsuite/schema/scoutsuite.py b/backend/app/integrations/scoutsuite/schema/scoutsuite.py new file mode 100644 index 00000000..dae8f40f --- /dev/null +++ b/backend/app/integrations/scoutsuite/schema/scoutsuite.py @@ -0,0 +1,48 @@ +from enum import Enum +from typing import List + +from fastapi import HTTPException +from pydantic import BaseModel +from pydantic import Field +from pydantic import root_validator + + +class ScoutSuiteReportOptions(str, Enum): + aws = "aws" + azure = "azure" + gcp = "gcp" + + +class ScoutSuiteReportOptionsResponse(BaseModel): + options: List[ScoutSuiteReportOptions] = Field( + ..., + description="The available report generation options", + example=["aws", "azure", "gcp"], + ) + success: bool + message: str + + +class AWSScoutSuiteReportRequest(BaseModel): + report_type: str = Field(..., description="The type of report to generate", example="aws") + access_key_id: str = Field(..., description="The AWS access key ID", example="AKIAIOSFODNN7EXAMPLE") + secret_access_key: str = Field(..., description="The AWS secret access key", example="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY") + report_name: str = Field(..., description="The name of the report", example="aws-report") + + @root_validator + def validate_report_type(cls, values): + report_type = values.get("report_type") + if report_type != ScoutSuiteReportOptions.aws: + raise HTTPException(status_code=400, detail="Invalid report type.") + return values + + +class ScoutSuiteReportResponse(BaseModel): + success: bool + message: str + + +class AvailableScoutSuiteReportsResponse(BaseModel): + success: bool + message: str + available_reports: List[str] diff --git a/backend/app/integrations/scoutsuite/services/scoutsuite.py b/backend/app/integrations/scoutsuite/services/scoutsuite.py new file mode 100644 index 00000000..43f0ea85 --- /dev/null +++ b/backend/app/integrations/scoutsuite/services/scoutsuite.py @@ -0,0 +1,50 @@ +import asyncio +import subprocess +from concurrent.futures import ThreadPoolExecutor + +from loguru import logger + +from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest + + +async def generate_aws_report_background(request: AWSScoutSuiteReportRequest): + logger.info("Generating AWS ScoutSuite report in the background") + + command = construct_aws_command(request) + await run_command_in_background(command) + + +def construct_aws_command(request: AWSScoutSuiteReportRequest): + """Construct the scout command.""" + return [ + "scout", + "aws", + "--access-key-id", + request.access_key_id, + "--secret-access-key", + request.secret_access_key, + "--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) + stdout, stderr = process.communicate() + + if process.returncode != 0: + logger.error(f"ScoutSuite report generation failed: {stderr.decode()}") + return None + + logger.info("ScoutSuite report generated successfully") + return None + + +async def run_command_in_background(command): + """Run the command in a separate thread.""" + with ThreadPoolExecutor() as executor: + loop = asyncio.get_event_loop() + await loop.run_in_executor(executor, lambda: run_command(command)) diff --git a/backend/app/routers/scoutsuite.py b/backend/app/routers/scoutsuite.py new file mode 100644 index 00000000..2c48f84b --- /dev/null +++ b/backend/app/routers/scoutsuite.py @@ -0,0 +1,13 @@ +from fastapi import APIRouter + +from app.integrations.scoutsuite.routes.scoutsuite import integration_scoutsuite_router + +# Instantiate the APIRouter +router = APIRouter() + +# Include the ScoutSuite related routes +router.include_router( + integration_scoutsuite_router, + prefix="/scoutsuite", + tags=["ScoutSuite"], +) diff --git a/backend/copilot.py b/backend/copilot.py index f15cd010..362fe928 100644 --- a/backend/copilot.py +++ b/backend/copilot.py @@ -7,6 +7,7 @@ from fastapi import HTTPException from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles from loguru import logger from app.auth.utils import AuthHandler @@ -55,6 +56,7 @@ from app.routers import office365 from app.routers import sap_siem from app.routers import scheduler +from app.routers import scoutsuite from app.routers import shuffle from app.routers import smtp from app.routers import stack_provisioning @@ -141,6 +143,7 @@ api_router.include_router(carbonblack.router) api_router.include_router(network_connectors.router) api_router.include_router(crowdstrike.router) +api_router.include_router(scoutsuite.router) # Include the APIRouter in the FastAPI app app.include_router(api_router) @@ -168,6 +171,13 @@ async def init_db(): scheduler.start() +# Create `scoutsuite-report` directory if it doesnt exist +if not os.path.exists("scoutsuite-report"): + os.makedirs("scoutsuite-report") + +app.mount("/scoutsuite-report", StaticFiles(directory="scoutsuite-report"), name="scoutsuite-report") + + @app.get("/") def hello(): return {"message": "CoPilot - We Made It!"} diff --git a/backend/requirements.txt b/backend/requirements.txt index e7a97caf..49f6d88f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -137,6 +137,7 @@ rfc3339-validator==0.1.4 rfc3986-validator==0.1.1 rich==13.6.0 rsa==4.9 +ScoutSuite==5.14.0 setuptools==65.5.0 simplejson==3.19.1 six==1.16.0 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 864bf846..8c30a758 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,7 +14,6 @@ "@fontsource/lexend": "^5.0.20", "@fontsource/public-sans": "^5.0.18", "@popperjs/core": "^2.11.8", - "@rollup/rollup-linux-x64-gnu": "*", "@shikijs/markdown-it": "^1.6.2", "@vueuse/components": "^10.10.0", "@vueuse/core": "^10.10.0", diff --git a/frontend/package.json b/frontend/package.json index c5f1192b..06c4f42e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,11 +3,6 @@ "version": "1.0.0", "private": true, "type": "module", - "overrides": { - "secure-ls": { - "crypto-js": "^4.2.0" - } - }, "scripts": { "dev": "vite --host 0.0.0.0", "dev:debug": "DEBUG=vite:* vite", @@ -120,10 +115,15 @@ "vitest": "^1.6.0", "vue-tsc": "^2.0.19" }, - "engines": { - "node": ">=18.0.0" - }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.18.0" + }, + "overrides": { + "secure-ls": { + "crypto-js": "^4.2.0" + } + }, + "engines": { + "node": ">=18.0.0" } } diff --git a/frontend/src/api/cloudSecurityAssessment.ts b/frontend/src/api/cloudSecurityAssessment.ts new file mode 100644 index 00000000..551379e7 --- /dev/null +++ b/frontend/src/api/cloudSecurityAssessment.ts @@ -0,0 +1,20 @@ +import { type FlaskBaseResponse } from "@/types/flask.d" +import { HttpClient } from "./httpClient" +import type { ScoutSuiteReportPayload, ScoutSuiteReport } from "@/types/cloudSecurityAssessment.d" + +export default { + getAvailableScoutSuiteReports() { + return HttpClient.get( + `/scoutsuite/available-reports` + ) + }, + getScoutSuiteReportGenerationOptions() { + return HttpClient.get(`/scoutsuite/report-generation-options`) + }, + generateAwsScoutSuiteReport(payload: ScoutSuiteReportPayload) { + return HttpClient.post(`/scoutsuite/generate-aws-report`, { ...payload, report_type: "aws" }) + }, + deleteScoutSuiteReport(reportName: string) { + return HttpClient.delete(`/scoutsuite/delete-report/${reportName}`) + } +} diff --git a/frontend/src/api/customers.ts b/frontend/src/api/customers.ts index 3fe1a19b..6bb9ddf1 100644 --- a/frontend/src/api/customers.ts +++ b/frontend/src/api/customers.ts @@ -3,7 +3,7 @@ import { HttpClient } from "./httpClient" import type { Customer, CustomerAgentHealth, - CustomerDecomissionedData, + CustomerDecommissionedData, CustomerMeta, CustomerProvision, CustomerProvisioningDefaultSettings @@ -17,6 +17,7 @@ export interface CustomerAgentsHealthcheckQuery { } export interface ProvisioningDefaultSettingsPayload { + id: number clusterName: string clusterKey: string masterIp: string @@ -111,7 +112,7 @@ export default { ) }, decommissionCustomer(code: string) { - return HttpClient.post( + return HttpClient.post( `/customer_provisioning/decommission`, {}, { @@ -140,7 +141,7 @@ export default { return HttpClient.post< FlaskBaseResponse & { customer_provisioning_default_settings: CustomerProvisioningDefaultSettings } >(`/customer_provisioning/default_settings`, { - id: 0, + id: payload.id, cluster_name: payload.clusterName, cluster_key: payload.clusterKey, master_ip: payload.masterIp, @@ -152,7 +153,7 @@ export default { return HttpClient.put< FlaskBaseResponse & { customer_provisioning_default_settings: CustomerProvisioningDefaultSettings } >(`/customer_provisioning/default_settings`, { - id: 0, + id: payload.id, cluster_name: payload.clusterName, cluster_key: payload.clusterKey, master_ip: payload.masterIp, diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 028eb236..827a1ed6 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -20,6 +20,7 @@ import reporting from "./reporting" import license from "./license" import scheduler from "./scheduler" import networkConnectors from "./networkConnectors" +import cloudSecurityAssessment from "./cloudSecurityAssessment" export default { agents, @@ -43,5 +44,6 @@ export default { reporting, license, scheduler, - networkConnectors + networkConnectors, + cloudSecurityAssessment } diff --git a/frontend/src/components/cloudSecurityAssessment/AvailableReportsItem.vue b/frontend/src/components/cloudSecurityAssessment/AvailableReportsItem.vue new file mode 100644 index 00000000..2c98b80c --- /dev/null +++ b/frontend/src/components/cloudSecurityAssessment/AvailableReportsItem.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue b/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue new file mode 100644 index 00000000..192ac176 --- /dev/null +++ b/frontend/src/components/cloudSecurityAssessment/AvailableReportsList.vue @@ -0,0 +1,109 @@ + + diff --git a/frontend/src/components/cloudSecurityAssessment/CloudSecurityAssessmentButton.vue b/frontend/src/components/cloudSecurityAssessment/CloudSecurityAssessmentButton.vue new file mode 100644 index 00000000..053b640a --- /dev/null +++ b/frontend/src/components/cloudSecurityAssessment/CloudSecurityAssessmentButton.vue @@ -0,0 +1,26 @@ + + + diff --git a/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue b/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue new file mode 100644 index 00000000..904a27ce --- /dev/null +++ b/frontend/src/components/cloudSecurityAssessment/CreationReportForm.vue @@ -0,0 +1,245 @@ + + + diff --git a/frontend/src/components/customers/CustomerForm.vue b/frontend/src/components/customers/CustomerForm.vue index 31c4189a..8267bc12 100644 --- a/frontend/src/components/customers/CustomerForm.vue +++ b/frontend/src/components/customers/CustomerForm.vue @@ -3,7 +3,7 @@
-
+
-
+
loadingDefaultSettings.value || submittingDefault const message = useMessage() const form = ref>(getClearForm()) const formRef = ref(null) -const isNew = ref(true) +const entityId = ref(0) const rules: FormRules = { cluster_name: { @@ -146,14 +146,15 @@ function validate() { }) } -function getClearForm(settings?: Omit) { - return { +function getClearForm(settings?: Partial) { + const payload = { cluster_name: settings?.cluster_name || "", cluster_key: settings?.cluster_key || "", master_ip: settings?.master_ip || "", grafana_url: settings?.grafana_url || "", wazuh_worker_hostname: settings?.wazuh_worker_hostname || "" } + return payload } function reset() { @@ -170,9 +171,10 @@ function resetForm() { function submit() { submittingDefaultSettings.value = true - const method = isNew.value ? "setProvisioningDefaultSettings" : "updateProvisioningDefaultSettings" + const method = entityId.value ? "updateProvisioningDefaultSettings" : "setProvisioningDefaultSettings" const payload = { + id: entityId.value || 0, clusterName: form.value.cluster_name, clusterKey: form.value.cluster_key, masterIp: form.value.master_ip, @@ -183,7 +185,7 @@ function submit() { Api.customers[method](payload) .then(res => { if (res.data.success) { - isNew.value = false + entityId.value = res.data.customer_provisioning_default_settings.id message.success(res.data?.message || "Customer Provisioning Default Settings updated successfully") } else { message.warning(res.data?.message || "An error occurred. Please try again later.") @@ -224,7 +226,7 @@ function getProvisioningDefaultSettings() { .getProvisioningDefaultSettings() .then(res => { if (res.data.success) { - isNew.value = false + entityId.value = res.data.customer_provisioning_default_settings.id || 0 setForm(res.data?.customer_provisioning_default_settings) } }) diff --git a/frontend/src/components/profile/ProfileSettings.vue b/frontend/src/components/profile/ProfileSettings.vue index 73062297..e1cc28ad 100644 --- a/frontend/src/components/profile/ProfileSettings.vue +++ b/frontend/src/components/profile/ProfileSettings.vue @@ -5,7 +5,7 @@
General
- + @@ -47,14 +47,11 @@ const settingsStore = useSettingsStore() const h24 = dayjs().format("HH:mm") const h12 = dayjs().format("h:mm a") -const dateFormatsAvailables = settingsStore.dateFormatsAvailables.map(i => ({ label: i, value: i })) +const dateFormatsAvailable = settingsStore.dateFormatsAvailable.map(i => ({ label: i, value: i })) const currentSateFormat = settingsStore.rawDateFormat const hours24 = settingsStore.hours24 const formValue = ref({ - username: "sigmund67", - email: "sigmund67@gmail.com", - name: "Margie Dibbert", dateFormat: currentSateFormat, hours24 }) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 4621f916..18b41e5b 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -184,6 +184,12 @@ const router = createRouter({ component: () => import("@/views/Scheduler.vue"), meta: { title: "Scheduler", auth: true, roles: UserRole.All } }, + { + path: "/cloud-security-assessment", + name: "CloudSecurityAssessment", + component: () => import("@/views/CloudSecurityAssessment.vue"), + meta: { title: "Cloud Security Assessment", auth: true, roles: UserRole.All } + }, { path: "/license", meta: { diff --git a/frontend/src/stores/settings.ts b/frontend/src/stores/settings.ts index 7c56d16e..ee876f28 100644 --- a/frontend/src/stores/settings.ts +++ b/frontend/src/stores/settings.ts @@ -17,7 +17,7 @@ export const useSettingsStore = defineStore("settings", { } }, getters: { - dateFormatsAvailables(state) { + dateFormatsAvailable(state) { return state.dateFormats }, hours24(state) { diff --git a/frontend/src/types/cloudSecurityAssessment.d.ts b/frontend/src/types/cloudSecurityAssessment.d.ts new file mode 100644 index 00000000..dda42ee3 --- /dev/null +++ b/frontend/src/types/cloudSecurityAssessment.d.ts @@ -0,0 +1,8 @@ +export type ScoutSuiteReport = string + +export interface ScoutSuiteReportPayload { + report_type: string + access_key_id: string + secret_access_key: string + report_name: string +} diff --git a/frontend/src/types/customers.d.ts b/frontend/src/types/customers.d.ts index faff7f51..520eeabd 100644 --- a/frontend/src/types/customers.d.ts +++ b/frontend/src/types/customers.d.ts @@ -86,7 +86,7 @@ export interface CustomerProvision { dfir_iris_username: string } -export interface CustomerDecomissionedData { +export interface CustomerDecommissionedData { agents_deleted: string[] groups_deleted: string[] stream_deleted: string diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index 04b5d49d..e14e509b 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -4,6 +4,7 @@ import { isMobile as detectMobile } from "detect-touch-device" import { md5 } from "js-md5" import dayjs from "@/utils/dayjs" import type { OsTypesFull } from "@/types/common" +import _trim from "lodash/trim" // Transform File Instance in base64 string export function file2Base64(blob: Blob): Promise { @@ -109,3 +110,7 @@ export function price( return `${symbol}${price}` } + +export function getBaseUrl() { + return _trim(import.meta.env.VITE_API_URL, "/") +} diff --git a/frontend/src/views/CloudSecurityAssessment.vue b/frontend/src/views/CloudSecurityAssessment.vue new file mode 100644 index 00000000..fd90fc5a --- /dev/null +++ b/frontend/src/views/CloudSecurityAssessment.vue @@ -0,0 +1,9 @@ + + + diff --git a/frontend/src/views/Overview.vue b/frontend/src/views/Overview.vue index 19d5388f..e43c7d59 100644 --- a/frontend/src/views/Overview.vue +++ b/frontend/src/views/Overview.vue @@ -1,14 +1,15 @@