diff --git a/dashboard/src/components/ProvisionerSettings.tsx b/dashboard/src/components/ProvisionerSettings.tsx index c9891b2869..37ac43a690 100644 --- a/dashboard/src/components/ProvisionerSettings.tsx +++ b/dashboard/src/components/ProvisionerSettings.tsx @@ -1,6 +1,7 @@ import React, { useContext, useEffect, useState } from "react"; import { AWSClusterNetwork, + CloudwatchAlarm, Cluster, Contract, EKS, @@ -27,11 +28,12 @@ import { useIntercom } from "lib/hooks/useIntercom"; import api from "shared/api"; import { Context } from "shared/Context"; import { pushFiltered } from "shared/routing"; -import { type ClusterType, type ClusterState } from "shared/types"; +import { type ClusterState, type ClusterType } from "shared/types"; import { PREFLIGHT_TO_ENUM } from "shared/util"; import info from "assets/info-outlined.svg"; import healthy from "assets/status-healthy.png"; +import GPUProvisionSettings from "./GPUProvisionSettings"; import Loading from "./Loading"; import Button from "./porter/Button"; import Checkbox from "./porter/Checkbox"; @@ -43,9 +45,6 @@ import Text from "./porter/Text"; import Tooltip from "./porter/Tooltip"; import VerticalSteps from "./porter/VerticalSteps"; import PreflightChecks from "./PreflightChecks"; -import { Integer } from "type-fest"; -import InputSlider from "./porter/InputSlider"; -import GPUProvisionSettings from "./GPUProvisionSettings"; const regionOptions = [ { value: "us-east-1", label: "US East (N. Virginia) us-east-1" }, @@ -169,6 +168,9 @@ const ProvisionerSettings: React.FC = (props) => { const [controlPlaneLogs, setControlPlaneLogs] = useState( new EKSLogging() ); + const [cloudwatchAlarm, setCloudwatchAlarm] = useState( + new CloudwatchAlarm() + ); const markStepStarted = async ( step: string, @@ -187,7 +189,7 @@ const ProvisionerSettings: React.FC = (props) => { project_id: currentProject ? currentProject.id : 0, } ); - } catch (err) { } + } catch (err) {} }; const getStatus = (): React.ReactNode => { @@ -264,17 +266,19 @@ const ProvisionerSettings: React.FC = (props) => { } // Split the input string by comma, then reduce the resulting array to an object - const tags = tagString.split(",").reduce>((obj, item) => { - // Split each item by "=", and trim whitespace from both key and value - const [key, value] = item.split("=").map(part => part.trim()); - - // Only add the key-value pair to the object if both key and value are present - if (key && value) { - obj[key] = value; - } + const tags = tagString + .split(",") + .reduce>((obj, item) => { + // Split each item by "=", and trim whitespace from both key and value + const [key, value] = item.split("=").map((part) => part.trim()); + + // Only add the key-value pair to the object if both key and value are present + if (key && value) { + obj[key] = value; + } - return obj; - }, {}); + return obj; + }, {}); return tags; } @@ -314,7 +318,6 @@ const ProvisionerSettings: React.FC = (props) => { } } - const nodeGroups = [ new EKSNodeGroup({ instanceType: "t3.medium", @@ -344,17 +347,18 @@ const ProvisionerSettings: React.FC = (props) => { // Conditionally add the last EKSNodeGroup if gpuModal is enabled if (props.gpuModal) { - nodeGroups.push(new EKSNodeGroup({ - instanceType: clusterState.gpuInstanceType, - minInstances: clusterState.gpuMinInstances || 0, - maxInstances: clusterState.gpuMaxInstances || 5, - nodeGroupType: NodeGroupType.CUSTOM, - isStateful: false, - additionalPolicies: clusterState.additionalNodePolicies, - })); + nodeGroups.push( + new EKSNodeGroup({ + instanceType: clusterState.gpuInstanceType, + minInstances: clusterState.gpuMinInstances || 0, + maxInstances: clusterState.gpuMaxInstances || 5, + nodeGroupType: NodeGroupType.CUSTOM, + isStateful: false, + additionalPolicies: clusterState.additionalNodePolicies, + }) + ); } - const data = new Contract({ cluster: new Cluster({ projectId: currentProject.id, @@ -365,17 +369,20 @@ const ProvisionerSettings: React.FC = (props) => { case: "eksKind", value: new EKS({ clusterName: clusterState.clusterName, - clusterVersion: clusterState.clusterVersion || defaultClusterVersion, + clusterVersion: + clusterState.clusterVersion || defaultClusterVersion, cidrRange: clusterState.cidrRangeVPC || defaultCidrVpc, // deprecated in favour of network.cidrRangeVPC: can be removed after december 2023 region: clusterState.awsRegion, loadBalancer: loadBalancerObj, logging: controlPlaneLogs, + cloudwatchAlarm, enableGuardDuty: clusterState.guardDutyEnabled, enableKmsEncryption: clusterState.kmsEncryptionEnabled, enableEcrScanning: clusterState.ecrScanningEnabled, network: new AWSClusterNetwork({ vpcCidr: clusterState.cidrRangeVPC || defaultCidrVpc, - serviceCidr: clusterState.cidrRangeServices || defaultCidrServices, + serviceCidr: + clusterState.cidrRangeServices || defaultCidrServices, }), nodeGroups, }), @@ -422,15 +429,13 @@ const ProvisionerSettings: React.FC = (props) => { pushFiltered(props, "/cluster-dashboard", ["project_id"], { cluster: cluster.name, }); - } - else { + } else { if (props.closeModal) { props.closeModal(); } } } }); - }) .catch((err) => { if (err) { @@ -461,8 +466,8 @@ const ProvisionerSettings: React.FC = (props) => { useEffect(() => { setIsReadOnly( props.clusterId && - (currentCluster.status === "UPDATING" || - currentCluster.status === "UPDATING_UNAVAILABLE") + (currentCluster.status === "UPDATING" || + currentCluster.status === "UPDATING_UNAVAILABLE") ); handleClusterStateChange( "clusterName", @@ -523,8 +528,8 @@ const ProvisionerSettings: React.FC = (props) => { const awsTags = eksValues.loadBalancer.tags ? Object.entries(eksValues.loadBalancer.tags) - .map(([key, value]) => `${key}=${value}`) - .join(",") + .map(([key, value]) => `${key}=${value}`) + .join(",") : ""; handleClusterStateChange("awsTags", awsTags); @@ -551,6 +556,14 @@ const ProvisionerSettings: React.FC = (props) => { setControlPlaneLogs(logging); } + if (eksValues.cloudwatchAlarm != null) { + const cloudwatchAlarm = new CloudwatchAlarm({ + emails: eksValues.cloudwatchAlarm.emails, + enable: eksValues.cloudwatchAlarm.enable, + }); + setCloudwatchAlarm(cloudwatchAlarm); + } + handleClusterStateChange("guardDutyEnabled", eksValues.enableGuardDuty); handleClusterStateChange( "kmsEncryptionEnabled", @@ -580,7 +593,6 @@ const ProvisionerSettings: React.FC = (props) => { setStep(0); } } - } } }, [clusterState]); @@ -1072,11 +1084,11 @@ const ProvisionerSettings: React.FC = (props) => { {(clusterState.wafV2ARN === undefined || clusterState.wafV2ARN?.length === 0) && ( - - error - {"Required if WafV2 is enabled"} - - )} + + error + {"Required if WafV2 is enabled"} + + )} )} @@ -1119,18 +1131,8 @@ const ProvisionerSettings: React.FC = (props) => { } > - Install AWS GuardDuty agent on this cluster (see details - to fully enable) + Install AWS GuardDuty agent on this cluster - - - - @@ -1185,10 +1187,9 @@ const ProvisionerSettings: React.FC = (props) => { setShowHelpMessage(false); try { await preflightChecks(); - } catch (err) { } + } catch (err) {} }; - const renderForm = (): JSX.Element => { // Render simplified form if initial create if (!props.clusterId) { @@ -1236,8 +1237,8 @@ const ProvisionerSettings: React.FC = (props) => { - - ) : + + ) : ( <> = (props) => { {preflightFailed && preflightData && ( <> - {(showHelpMessage && currentProject?.quota_increase) ? <> - - Your account currently is blocked from provisioning in {clusterState.awsRegion} due to a quota limit imposed by AWS. Either change the region or request to increase quotas. - - - - Porter can automatically request quota increases on your behalf and email you once the cluster is provisioned. - - -
- + + +
+ + ) : ( + <> + + Your account currently is blocked from + provisioning in {clusterState.awsRegion} due to a + quota limit imposed by AWS. Either change the + region or request to increase quotas. + + - - - : ( - <> - Your account currently is blocked from provisioning in {clusterState.awsRegion} due to a quota limit imposed by AWS. Either change the region or request to increase quotas. - )} - )} - } - , <> + + + )} + + )} + + )} + , + <> Provision your cluster - {showEmailMessage && <> - - After your quota requests have been approved by AWS, Porter will email you when your cluster has been provisioned. - - - } + {showEmailMessage && ( + <> + + After your quota requests have been approved by AWS, + Porter will email you when your cluster has been + provisioned. + + + + )} - - , - + + , ].filter((x) => x)} /> - ) + ); } // If settings, update full form @@ -1330,10 +1358,9 @@ const ProvisionerSettings: React.FC = (props) => { showEmailMessage={showEmailMessage} requestQuotasAndProvision={requestQuotaIncrease} /> - ) + ); } - return ( <> @@ -1390,7 +1417,7 @@ const ExpandHeader = styled.div<{ isExpanded: boolean }>` margin - right: 7px; margin-left: -7px; transform: ${(props) => - props.isExpanded ? "rotate(0deg)" : "rotate(-90deg)"}; + props.isExpanded ? "rotate(0deg)" : "rotate(-90deg)"}; transition: transform 0.1s ease; } `; diff --git a/dashboard/src/components/SOC2Checks.tsx b/dashboard/src/components/SOC2Checks.tsx index 995258b5e7..895ae5d00e 100644 --- a/dashboard/src/components/SOC2Checks.tsx +++ b/dashboard/src/components/SOC2Checks.tsx @@ -4,6 +4,7 @@ import styled from "styled-components"; import Container from "components/porter/Container"; +import { type Soc2Check, type Soc2Data } from "shared/types"; import external_link from "assets/external-link.svg"; import failure from "assets/failure.svg"; import pending from "assets/pending.svg"; @@ -14,13 +15,13 @@ import Link from "./porter/Link"; import Spacer from "./porter/Spacer"; import Text from "./porter/Text"; import ToggleRow from "./porter/ToggleRow"; -import { type Soc2Data, type Soc2Check } from "shared/types"; +import SOC2EmailComponent from "./SOC2EmailComponent"; type Props = RouteComponentProps & { - soc2Data: Soc2Check; + soc2Data: Soc2Data; error?: string; enableAll: boolean; - setSoc2Data: (x: Soc2Check) => void; + setSoc2Data: (x: Soc2Data) => void; readOnly: boolean; }; type ItemProps = RouteComponentProps & { @@ -63,8 +64,8 @@ const SOC2Checks: React.FC = ({ status: !soc2Checks[key].enabled ? "" : soc2Checks[key].status === "PENDING_ENABLED" - ? "PENDING_ENABLED" - : "ENABLED", + ? "PENDING_ENABLED" + : "ENABLED", }; return acc; }, {}); @@ -76,8 +77,8 @@ const SOC2Checks: React.FC = ({ }, [enableAll]); const Soc2Item: React.FC = ({ checkKey, checkLabel }) => { - const checkData = soc2Data?.soc2_checks?.[checkKey]; - const hasMessage = checkData?.message; + const checkData: Soc2Check = soc2Data?.soc2_checks?.[checkKey]; + const hasMessage: S = checkData?.message; const enabled = checkData?.enabled; const status = checkData?.status; @@ -174,8 +175,8 @@ const SOC2Checks: React.FC = ({ readOnly ? "Wait for provisioning to complete before editing this field." : enableAll - ? "Global SOC 2 setting must be disabled to toggle this" - : checkData?.disabledTooltip + ? "Global SOC 2 setting must be disabled to toggle this" + : checkData?.disabledTooltip } > @@ -196,6 +197,17 @@ const SOC2Checks: React.FC = ({ )} + {checkData.email && (checkData.enabled || enableAll) && ( + <> + + + + )} )} @@ -207,9 +219,6 @@ const SOC2Checks: React.FC = ({ return ( <> <> - {/*
- -
*/} {Array.from(combinedKeys).map((checkKey) => ( @@ -256,7 +265,7 @@ const CheckItemContainer = styled.div` props.isExpanded ? "2px solid #3a48ca" : "1px solid " + - props.theme.border}; // Thicker and blue border if expanded + props.theme.border}; // Thicker and blue border if expanded border-radius: 5px; font-size: 13px; width: 100%; diff --git a/dashboard/src/components/SOC2EmailComponent.tsx b/dashboard/src/components/SOC2EmailComponent.tsx new file mode 100644 index 0000000000..70ad8d7650 --- /dev/null +++ b/dashboard/src/components/SOC2EmailComponent.tsx @@ -0,0 +1,126 @@ +import React, { useState } from "react"; +import { emailRegex } from "shared/regex"; +import { type Soc2Data } from "shared/types"; +import styled from "styled-components"; +import Button from "./porter/Button"; +import Input from "./porter/Input"; +import Spacer from "./porter/Spacer"; +import Text from "./porter/Text"; + +type Props = { + enabled: boolean; + emails: string[]; + setSoc2Data: (x: Soc2Data) => void; + soc2CheckKey: string; +}; + +const SOC2EmailComponent: React.FC = ({ emails, setSoc2Data, soc2CheckKey }) => { + const [email, setEmail] = useState(""); + const [emailError, setEmailError] = useState(false); + + const addEmail = (): void => { + if (emailRegex.test(email)) { + const updatedEmails = [...emails, email]; + + setSoc2Data((prev) => ({ + ...prev, + soc2_checks: { + ...prev.soc2_checks, + [soc2CheckKey]: { + ...prev.soc2_checks[soc2CheckKey], + email: updatedEmails, + }, + }, + })); + setEmail(''); + } else { + setEmailError(true); + } + }; + + const deleteEmail = (emailToDelete: string): void => { + const updatedEmails = emails.filter(e => e !== emailToDelete); + setSoc2Data((prev) => ({ + ...prev, + soc2_checks: { + ...prev.soc2_checks, + [soc2CheckKey]: { + ...prev.soc2_checks[soc2CheckKey], + email: updatedEmails, + }, + }, + })); + }; + + return ( + <> +
+ { + setEmail(x); + setEmailError(false); + }} + width="60%" + error={emailError && "Please enter a valid email"} + /> + + +
+ +
+ {emails.length > 0 && Subscribers: } + {emails.map((email, index) => ( + + {email} + deleteEmail(email)}> + delete + + + ))} +
+ + ); +}; + +export default SOC2EmailComponent; + + +const DeleteButton = styled.div` + display: flex; + visibility: ${(props: { invis?: boolean }) => + props.invis ? "hidden" : "visible"}; + align-items: center; + justify-content: center; + width: 30px; + margin-right: 15px; + float: right; + height: 30px; + :hover { + background: #ffffff11; + border-radius: 20px; + cursor: pointer; + } + + > i { + font-size: 20px; + color: #ffffff44; + border-radius: 20px; + } +`; + +const EmailItem = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + padding: 2px; + margin-bottom: 5px; + border-radius: 4px; + max-width: 38%; + +`; diff --git a/dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx b/dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx index df373971ac..0c3556dd67 100644 --- a/dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx +++ b/dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx @@ -1,6 +1,12 @@ import React, { useContext, useEffect, useMemo, useState } from "react"; import type { JsonValue } from "@bufbuild/protobuf"; -import { Cluster, Contract, EKS, EKSLogging } from "@porter-dev/api-contracts"; +import { + CloudwatchAlarm, + Cluster, + Contract, + EKS, + EKSLogging, +} from "@porter-dev/api-contracts"; import axios from "axios"; import styled from "styled-components"; import { match } from "ts-pattern"; @@ -17,10 +23,10 @@ import SOC2Checks from "components/SOC2Checks"; import api from "shared/api"; import { Context } from "shared/Context"; +import { type Soc2Data } from "shared/types"; import sparkle from "assets/sparkle.svg"; import DonutChart from "./DonutChart"; -import { Soc2Data } from "shared/types"; type Props = { credentialId: string; @@ -39,6 +45,7 @@ type Props = { // "locked":(true if unmutable field like KMS), // "disabledTooltip": "display if message is disabled", // "hideToggle": true (if you want to hide the toggle +// "email": ["example@porter"] (if you want to add an email field to pass in) // } const soc2DataDefault: Soc2Data = { soc2_checks: { @@ -46,7 +53,8 @@ const soc2DataDefault: Soc2Data = { message: "Porter-provisioned instances do not allow remote SSH access. Users are not allowed to invoke commands directly on the host, and all commands are invoked via the EKS Control Plane.", enabled: true, - hideToggle: true, + locked: true, + disabledTooltip: "Enabled by default by Porter", status: "ENABLED", }, "Cluster Secret Encryption": { @@ -74,6 +82,22 @@ const soc2DataDefault: Soc2Data = { info: "", status: "", }, + "Cloudwatch Alarm Creation": { + message: + "AWS Cloudwatch Alarms will be created for various components in your infrastructure provisioned by Porter. Alarm notifications will be sent on behalf of Porter via an AWS SNS Topic to the provided email addresses. Email addresses must be manually confirmed in the respective inboxes before notifications will be sent.", + enabled: false, + info: "", + status: "", + email: [], // this is a special case for email + }, + "Intrusion Detection": { + message: + "Amazon GuardDuty is a threat detection service offered by AWS that continuously monitors and analyzes your AWS account for malicious activity and unauthorized behavior. By leveraging machine learning, anomaly detection, and threat intelligence, GuardDuty provides real-time alerts, helping you proactively identify and respond to security threats, ultimately enhancing the overall security posture of your AWS environment.", + link: "https://docs.aws.amazon.com/guardduty/latest/ug/what-is-guardduty.html", + enabled: false, + info: "", + status: "", + }, }, }; @@ -123,7 +147,6 @@ const Compliance: React.FC = (props) => { ); const contract = createContract(result.base64_contract); - await api.createContract("", contract, { project_id: currentProject.id, }); @@ -160,6 +183,11 @@ const Compliance: React.FC = (props) => { soc2Data.soc2_checks["Cluster Secret Encryption"].enabled; const ecrScanningEnabled = soc2Data.soc2_checks["Enhanced Image Vulnerability Scanning"].enabled; + const snsMonitoringEnabled = + soc2Data.soc2_checks["Cloudwatch Alarm Creation"].enabled; + const snsMonitoringEmails = + soc2Data.soc2_checks["Cloudwatch Alarm Creation"].email; + const enableGuardDuty = soc2Data.soc2_checks["Intrusion Detection"].enabled; const contractData = JSON.parse(atob(base64Contract)); const latestCluster: Cluster = Cluster.fromJson(contractData.cluster, { @@ -176,6 +204,8 @@ const Compliance: React.FC = (props) => { ecrScanningEnabled || value.enableEcrScanning || false, + enableGuardDuty: + soc2Enabled || enableGuardDuty || value.enableGuardDuty || false, logging: new EKSLogging({ enableApiServerLogs: soc2Enabled || cloudTrailEnabled || false, enableAuditLogs: soc2Enabled || cloudTrailEnabled || false, @@ -185,6 +215,10 @@ const Compliance: React.FC = (props) => { soc2Enabled || cloudTrailEnabled || false, enableSchedulerLogs: soc2Enabled || cloudTrailEnabled || false, }), + cloudwatchAlarm: new CloudwatchAlarm({ + enable: soc2Enabled || snsMonitoringEnabled || false, + emails: snsMonitoringEmails || [], + }), }), case: "eksKind" as const, })) @@ -236,11 +270,26 @@ const Compliance: React.FC = (props) => { return ""; }; + // function to check if any fields are missing + const missingFields = (): boolean => { + const checks = soc2Data.soc2_checks; + for (const key in checks) { + if ( + (checks[key].enabled || soc2Enabled) && + checks[key].email?.length === 0 + ) { + return true; + } + } + return false; + }; + const isDisabled = (): boolean | undefined => { return ( isUserProvisioning || isClicked || - (currentCluster && !currentProject?.enable_reprovision) + (currentCluster && !currentProject?.enable_reprovision) || + missingFields() ); }; @@ -261,7 +310,7 @@ const Compliance: React.FC = (props) => { project_id: currentProject ? currentProject.id : 0, } ); - } catch (err) { } + } catch (err) {} }; const isUserProvisioning = useMemo(() => { @@ -310,19 +359,34 @@ const Compliance: React.FC = (props) => { }, "Enhanced Image Vulnerability Scanning": { ...prevSoc2Data.soc2_checks[ - "Enhanced Image Vulnerability Scanning" + "Enhanced Image Vulnerability Scanning" ], enabled: eksValues.enableEcrScanning, status: determineStatus(eksValues.enableEcrScanning), }, + "Cloudwatch Alarm Creation": { + ...prevSoc2Data.soc2_checks["Cloudwatch Alarm Creation"], + enabled: eksValues.cloudwatchAlarm?.enable || false, + status: determineStatus( + eksValues.cloudwatchAlarm?.enable || false + ), + email: eksValues.cloudwatchAlarm?.emails || [], + }, + "Intrusion Detection": { + ...prevSoc2Data.soc2_checks["Intrusion Detection"], + enabled: eksValues.enableGuardDuty, + status: determineStatus(eksValues.enableGuardDuty), + }, }, }; }); + // if new control is added add its individual enabled field to align with the enabled field here setSoc2Enabled( cloudTrailEnabled && - eksValues.enableKmsEncryption && - eksValues.enableEcrScanning + eksValues.enableKmsEncryption && + eksValues.enableEcrScanning && + (eksValues.cloudwatchAlarm?.enable || false) ); } }, [props.selectedClusterVersion]); @@ -334,7 +398,7 @@ const Compliance: React.FC = (props) => { setIsReadOnly( currentCluster.status === "UPDATING" || - currentCluster.status === "UPDATING_UNAVAILABLE" + currentCluster.status === "UPDATING_UNAVAILABLE" ); }, []); @@ -366,6 +430,9 @@ const Compliance: React.FC = (props) => { disabled={isDisabled() ?? isLoading} onClick={applySettings} status={getStatus()} + disabledTooltipMessage={ + "Missing fields. Please fill out all required fields to enable SOC 2 compliance." + } > Save settings diff --git a/dashboard/src/shared/Context.tsx b/dashboard/src/shared/Context.tsx index dcf0c70e7b..976acc38aa 100644 --- a/dashboard/src/shared/Context.tsx +++ b/dashboard/src/shared/Context.tsx @@ -70,9 +70,7 @@ export type GlobalContextType = { setShouldRefreshClusters: (shouldRefreshClusters: boolean) => void; featurePreview: boolean; setFeaturePreview: (featurePreview: boolean) => void; - soc2Data: any; - setSoc2Data: (x: any) => void; -}; +} /** * Component managing a universal (application-wide) data store. @@ -216,46 +214,7 @@ class ContextProvider extends Component { featurePreview: false, setFeaturePreview: (featurePreview) => { this.setState({ featurePreview }); - }, - soc2Data: { - preflight_checks: { - "Public SSH Access": { - message: - "Porter-provisioned instances do not allow remote SSH access. Users are not allowed to invoke commands directly on the host, and all commands are invoked via the EKS Control Plane.", - enabled: true, - hideToggle: true, - status: "ENABLED", - }, - "Cluster Secret Encryption": { - message: - "Cluster secrets can be encrypted using an AWS KMS Key. Secrets will be encrypted at rest, and encryption cannot be disabled for secrets.", - enabled: false, - disabledTooltip: - "Enable KMS encryption for the cluster to enable SOC 2 compliance.", - link: "https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-eks-adds-envelope-encryption-for-secrets-with-aws-kms/", - locked: true, - status: "", - }, - "Control Plane Log Retention": { - message: - "EKS Control Plane logs are by default available for a minimal amount of time, typically 1 hour or less. EKS CloudTrail Forwarding automatically sends control plane logs to CloudTrail for longer retention and later inspection.", - enabled: false, - enabledField: "Retain CloudTrail logs for 365 days", - status: "", - }, - "Enhanced Image Vulnerability Scanning": { - message: - "AWS ECR scans for CVEs from the open-source Clair database on push image push. Enhanced scanning provides continuous, automated scans against images as new vulnerabilities appear.", - link: "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-enhanced.html", - enabled: false, - info: "", - status: "", - }, - }, - }, - setSoc2Data: (soc2Data) => { - localStorage.setItem("soc2Data", JSON.stringify(soc2Data)); - }, + } }; render() { diff --git a/dashboard/src/shared/types.tsx b/dashboard/src/shared/types.tsx index f0bff2e113..de8d8c2dcc 100644 --- a/dashboard/src/shared/types.tsx +++ b/dashboard/src/shared/types.tsx @@ -429,8 +429,6 @@ export type ContextProps = { setEnableGitlab: (enableGitlab: boolean) => void; shouldRefreshClusters: boolean; setShouldRefreshClusters: (shouldRefreshClusters: boolean) => void; - soc2Data: any; - setSoc2Data: (x: any) => void; }; export enum JobStatusType { @@ -739,6 +737,7 @@ export type Soc2Check = { locked?: boolean; enabledField?: string; info?: string; + email?: string[]; }; export type Soc2Data = {