import React, { useState, useEffect } from "react"; import { useQuery } from "react-query"; import { Link } from "react-router"; import PATHS from "router/paths"; import { IJiraIntegration, IZendeskIntegration, IIntegration, IIntegrations, } from "interfaces/integration"; import { IConfig } from "interfaces/config"; import configAPI from "services/entities/config"; import ReactTooltip from "react-tooltip"; // @ts-ignore import Dropdown from "components/forms/fields/Dropdown"; import Modal from "components/Modal"; import Button from "components/buttons/Button"; import Slider from "components/forms/fields/Slider"; import Radio from "components/forms/fields/Radio"; // @ts-ignore import InputField from "components/forms/fields/InputField"; import { IWebhookSoftwareVulnerabilities } from "interfaces/webhook"; import useDeepEffect from "hooks/useDeepEffect"; import _, { size } from "lodash"; import PreviewPayloadModal from "../PreviewPayloadModal"; interface ISoftwareAutomations { webhook_settings: { vulnerabilities_webhook: IWebhookSoftwareVulnerabilities; }; integrations: { jira: IJiraIntegration[]; zendesk: IZendeskIntegration[]; }; } interface IManageAutomationsModalProps { onCancel: () => void; onCreateWebhookSubmit: (formData: ISoftwareAutomations) => void; togglePreviewPayloadModal: () => void; showPreviewPayloadModal: boolean; softwareVulnerabilityAutomationEnabled?: boolean; softwareVulnerabilityWebhookEnabled?: boolean; currentDestinationUrl?: string; recentVulnerabilityMaxAge?: number; } const validateWebhookURL = (url: string) => { const errors: { [key: string]: string } = {}; if (url === "") { errors.url = "Please add a destination URL"; } const valid = !size(errors); return { valid, errors }; }; const baseClass = "manage-automations-modal"; const ManageAutomationsModal = ({ onCancel: onReturnToApp, onCreateWebhookSubmit, togglePreviewPayloadModal, showPreviewPayloadModal, softwareVulnerabilityAutomationEnabled, softwareVulnerabilityWebhookEnabled, currentDestinationUrl, recentVulnerabilityMaxAge, }: IManageAutomationsModalProps): JSX.Element => { const [destinationUrl, setDestinationUrl] = useState( currentDestinationUrl || "" ); const [errors, setErrors] = useState<{ [key: string]: string }>({}); const [softwareAutomationsEnabled, setSoftwareAutomationsEnabled] = useState( softwareVulnerabilityAutomationEnabled || false ); const [integrationEnabled, setIntegrationEnabled] = useState( !softwareVulnerabilityWebhookEnabled ); const [jiraIntegrationsIndexed, setJiraIntegrationsIndexed] = useState< IIntegration[] >(); const [zendeskIntegrationsIndexed, setZendeskIntegrationsIndexed] = useState< IIntegration[] >(); const [allIntegrationsIndexed, setAllIntegrationsIndexed] = useState< IIntegration[] >(); const [ selectedIntegration, setSelectedIntegration, ] = useState(); useDeepEffect(() => { setSoftwareAutomationsEnabled( softwareVulnerabilityAutomationEnabled || false ); }, [softwareVulnerabilityAutomationEnabled]); useDeepEffect(() => { if (destinationUrl) { setErrors({}); } }, [destinationUrl]); const { data: integrations } = useQuery( ["integrations"], () => configAPI.loadAll(), { select: (data: IConfig) => { return data.integrations; }, onSuccess: (data) => { // Set jira and zendesk integrations const addJiraIndexed = data.jira ? data.jira.map((integration, index) => { return { ...integration, originalIndex: index, type: "jira" }; }) : []; setJiraIntegrationsIndexed(addJiraIndexed); const addZendeskIndexed = data.zendesk ? data.zendesk.map((integration, index) => { return { ...integration, originalIndex: index, type: "zendesk", }; }) : []; setZendeskIntegrationsIndexed(addZendeskIndexed); }, } ); useEffect(() => { if (jiraIntegrationsIndexed && zendeskIntegrationsIndexed) { const combineDataSets = jiraIntegrationsIndexed.concat( zendeskIntegrationsIndexed ); setAllIntegrationsIndexed( combineDataSets?.map((integration, index) => { return { ...integration, dropdownIndex: index }; }) ); } }, [ jiraIntegrationsIndexed, zendeskIntegrationsIndexed, setAllIntegrationsIndexed, ]); useEffect(() => { if (allIntegrationsIndexed) { const currentSelectedIntegration = allIntegrationsIndexed.find( (integration) => { return integration.enable_software_vulnerabilities === true; } ); setSelectedIntegration(currentSelectedIntegration); } }, [allIntegrationsIndexed]); const onURLChange = (value: string) => { setDestinationUrl(value); }; const handleSaveAutomation = (evt: React.MouseEvent) => { evt.preventDefault(); const { valid: validUrl, errors: newErrors } = validateWebhookURL( destinationUrl ); setErrors({ ...errors, ...newErrors, }); // Original config keys for software automation (webhook_settings, integrations) const configSoftwareAutomations: ISoftwareAutomations = { webhook_settings: { vulnerabilities_webhook: { destination_url: destinationUrl, enable_vulnerabilities_webhook: softwareVulnerabilityWebhookEnabled, }, }, integrations: { jira: integrations?.jira || [], zendesk: integrations?.zendesk || [], }, }; const updateSoftwareAutomation = () => { if (!softwareAutomationsEnabled) { // set enable_vulnerabilities_webhook // jira.enable_software_vulnerabilities // and zendesk.enable_software_vulnerabilities to false configSoftwareAutomations.webhook_settings.vulnerabilities_webhook.enable_vulnerabilities_webhook = false; const disableAllJira = configSoftwareAutomations.integrations.jira.map( (integration) => { return { ...integration, enable_software_vulnerabilities: false }; } ); configSoftwareAutomations.integrations.jira = disableAllJira; const disableAllZendesk = configSoftwareAutomations.integrations.zendesk.map( (integration) => { return { ...integration, enable_software_vulnerabilities: false, }; } ); configSoftwareAutomations.integrations.zendesk = disableAllZendesk; return; } if (!integrationEnabled) { if (!validUrl) { return; } // set enable_vulnerabilities_webhook to true // all jira.enable_software_vulnerabilities to false // all zendesk.enable_software_vulnerabilities to false configSoftwareAutomations.webhook_settings.vulnerabilities_webhook.enable_vulnerabilities_webhook = true; const disableAllJira = configSoftwareAutomations.integrations.jira.map( (integration) => { return { ...integration, enable_software_vulnerabilities: false, }; } ); configSoftwareAutomations.integrations.jira = disableAllJira; const disableAllZendesk = configSoftwareAutomations.integrations.zendesk.map( (integration) => { return { ...integration, enable_software_vulnerabilities: false, }; } ); configSoftwareAutomations.integrations.zendesk = disableAllZendesk; return; } // set enable_vulnerabilities_webhook to false // all jira.enable_software_vulnerabilities to false // all zendesk.enable_software_vulnerabilities to false // except the one integration selected configSoftwareAutomations.webhook_settings.vulnerabilities_webhook.enable_vulnerabilities_webhook = false; const enableSelectedJiraIntegrationOnly = configSoftwareAutomations.integrations.jira.map( (integration, index) => { return { ...integration, enable_software_vulnerabilities: selectedIntegration?.type === "jira" ? index === selectedIntegration?.originalIndex : false, }; } ); configSoftwareAutomations.integrations.jira = enableSelectedJiraIntegrationOnly; const enableSelectedZendeskIntegrationOnly = configSoftwareAutomations.integrations.zendesk.map( (integration, index) => { return { ...integration, enable_software_vulnerabilities: selectedIntegration?.type === "zendesk" ? index === selectedIntegration?.originalIndex : false, }; } ); configSoftwareAutomations.integrations.zendesk = enableSelectedZendeskIntegrationOnly; }; updateSoftwareAutomation(); onCreateWebhookSubmit(configSoftwareAutomations); onReturnToApp(); }; const createIntegrationDropdownOptions = () => { const integrationOptions = allIntegrationsIndexed?.map((i) => { return { value: String(i.dropdownIndex), label: `${i.url} - ${i.project_key || i.group_id}`, }; }); return integrationOptions; }; const onChangeSelectIntegration = (selectIntegrationIndex: string) => { const integrationWithIndex: | IIntegration | undefined = allIntegrationsIndexed?.find( (integ: IIntegration) => integ.dropdownIndex === parseInt(selectIntegrationIndex, 10) ); setSelectedIntegration(integrationWithIndex); }; const onRadioChange = ( enableIntegration: boolean ): ((evt: string) => void) => { return () => { setIntegrationEnabled(enableIntegration); }; }; const renderTicket = () => { return (

A ticket will be created in your Integration if a detected vulnerability (CVE) was published in the last{" "} {recentVulnerabilityMaxAge || "30"} days.

{(jiraIntegrationsIndexed && jiraIntegrationsIndexed.length > 0) || (zendeskIntegrationsIndexed && zendeskIntegrationsIndexed.length > 0) ? ( ) : (
You have no integrations.
Add integration
)}
); }; const renderWebhook = () => { return (

A request will be sent to your configured Destination URL if a detected vulnerability (CVE) was published in the last{" "} {recentVulnerabilityMaxAge || "30"} days.

); }; if (showPreviewPayloadModal) { return ; } return (
setSoftwareAutomationsEnabled(!softwareAutomationsEnabled) } inactiveText={"Vulnerability automations disabled"} activeText={"Vulnerability automations enabled"} />
Workflow
{integrationEnabled ? renderTicket() : renderWebhook()}
{!softwareAutomationsEnabled && (
)}
<> Add an integration to create
tickets for vulnerability automations.
); }; export default ManageAutomationsModal;