diff --git a/packages/web/src/assets/images/audit_wizard.png b/packages/web/src/assets/images/audit_wizard.png new file mode 100644 index 000000000..86e959765 Binary files /dev/null and b/packages/web/src/assets/images/audit_wizard.png differ diff --git a/packages/web/src/constants/constants.ts b/packages/web/src/constants/constants.ts index 4a21f267b..43363dcf6 100644 --- a/packages/web/src/constants/constants.ts +++ b/packages/web/src/constants/constants.ts @@ -26,6 +26,8 @@ export const MAX_NFT_TIER = 3; export const stagingServiceUrl = "https://hats-backend-dev.herokuapp.com/v1"; export const prodServiceUrl = "https://hats-backend-prod.herokuapp.com/v1"; +export const auditWizardVerifyService = "https://app.auditwizard.io:5000/submissions/finalizeHats"; + export const NFTContractDataProxy = { ["0xCCaadc293FaAEa229e0ca4A22B0330b65634b483".toLowerCase()]: "0x1d25bf3d0f8997282055a1a242fa2b146e7b4ec5", ["0x571f39d351513146248AcafA9D0509319A327C4D".toLowerCase()]: "0xe127be2bc276142039bc16251bb04e15b2b34f25", diff --git a/packages/web/src/languages/en.json b/packages/web/src/languages/en.json index d65854669..268eef84f 100644 --- a/packages/web/src/languages/en.json +++ b/packages/web/src/languages/en.json @@ -293,7 +293,7 @@ "noOptions": "No options", "success": "Success", "failed": "Failed", - "peding": "Pending", + "pending": "Pending", "finishingVaultCreation": "Finishing vault creation", "weAreFinishingTheCreationOfVault": " Thanks for creating a new vault in Hats! \n\n We are finishing the creation of your vault. We need a couple more minutes. \n\n We will notify you via email you when the vault is created.", "executeTxOnGnosisForCreatingVault": " Thanks for creating a new vault in Hats! \n\n You just create the proposal for creating the vault in Safe app. Please approve and execute the transaction on the Safe app. \n\n We will notify you via email you when the vault is created.", @@ -582,6 +582,18 @@ "goToProjectWebsite": "Go to project website", "doYouWantToGoToProjectWebsite": "Do you want to go to project website? \n ({{website}})", "yesGo": "Yes, go", + "clearSubmission": "Clear submission", + "clearSubmissionExplanation": "Are you sure you want to clear this submission? \n\n This will remove all the information of the submission.", + "clearForm": "Clear form", + "existingSubmission": "Existing submission", + "clearExistingSubmissionAuditWizardExplanation": "Attention, you have started to create a submission, in order to submit a new one from AuditWizard you need to remove the current one. \n\nAre you sure you want to clear this submission? \n This will remove all the information of the submission.", + "projectNotAvailable": "Project not available", + "projectNotAvailableExplanation": "The project on which you are attempting to submit a vulnerability is no longer available. \n\n If you think this is an error, please contact Hats support.", + "understand": "Understand", + "submissionChanged": "Submission changed", + "submissionChangedExplanationAuditWizard": "The submission you are trying to edit has changed. \n\n If you want to edit the submission, please go back to Audit Wizard.", + "submissionNotValid": "Submission not valid", + "submissionNotValidExplanationAuditWizard": "The submission you are trying to edit is not valid. \n\n Please go back to Audit Wizard and try again.", "PGPTool": { "title": "PGP tool", "unlockPgpTool": "Unlock the PGP tool", @@ -829,6 +841,8 @@ "encryptedSubmission": "Encrypted submission", "encryptedSubmissionExplanation": "The submission is encrypted and only the committee can decrypt it.", "decryptedSubmissionExplanation": "The submission is decrypted and public. This normally happens on Audit Competitions.", + "submissionSubmittedViaAuditWizard": "Submission submitted via auditwizard.io", + "editSubmission": "Edit submission", "terms": { "bugBounty": { "submissionSection": "

Your submission will be sent to the committee who will process your submission.

The committee of the vault will respond within 12 hours to acknowledge the receipt of submission via the communication channel you provided.

", diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionContactInfo/SubmissionContactInfo.tsx b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionContactInfo/SubmissionContactInfo.tsx index 296a78be4..e37324085 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionContactInfo/SubmissionContactInfo.tsx +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionContactInfo/SubmissionContactInfo.tsx @@ -13,7 +13,7 @@ import { StyledContactInfo } from "./styles"; export function SubmissionContactInfo() { const { t } = useTranslation(); const { address: account } = useAccount(); - const { submissionData, setSubmissionData, vault } = useContext(SubmissionFormContext); + const { submissionData, setSubmissionData, vault, allFormDisabled } = useContext(SubmissionFormContext); const isAuditCompetition = vault?.description?.["project-metadata"].type === "audit"; @@ -44,7 +44,7 @@ export function SubmissionContactInfo() { setSubmissionData((prev) => { if (prev) return { - ...prev!, + ...prev, contact: { ...contactData, verified: true }, submissionsDescriptions: { ...prev.submissionsDescriptions, verified: false }, submissionResult: undefined, @@ -70,6 +70,7 @@ export function SubmissionContactInfo() { {t("Submissions.addGithubAccountConnectIssue")}

(); const isAuditSubmission = vault?.description?.["project-metadata"].type === "audit"; @@ -124,6 +124,7 @@ export function SubmissionDescriptions() { const { encryptedData, sessionKey } = encryptionResult; const submissionInfo = { + ref: submissionData.ref, decrypted, encrypted: encryptedData as string, }; @@ -178,6 +179,7 @@ export function SubmissionDescriptions() {
( (field.name, dirtyFields, defaultValues)} error={error} label={t("severity")} @@ -204,6 +207,7 @@ export function SubmissionDescriptions() { name={`descriptions.${index}.description`} render={({ field, fieldState: { error }, formState: { dirtyFields, defaultValues } }) => ( (field.name, dirtyFields, defaultValues)} error={error} colorable @@ -212,7 +216,7 @@ export function SubmissionDescriptions() { )} /> - {!submissionDescription.isEncrypted && ( + {!submissionDescription.isEncrypted && !allFormDisabled && ( )} - {controlledDescriptions.length > 1 && ( + {controlledDescriptions.length > 1 && !allFormDisabled && (
+ {!allFormDisabled && ( + + )}
diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionDescriptions/utils.ts b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionDescriptions/utils.ts index d77d93a78..98925a0e9 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionDescriptions/utils.ts +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionDescriptions/utils.ts @@ -88,7 +88,7 @@ export const getGithubIssueDescription = ( submissionData: ISubmissionData, description: ISubmissionsDescriptionsData["descriptions"][0] ) => { - return ` + return `${submissionData.ref === "audit-wizard" ? "***Submitted via auditwizard.io***\n" : ""} **Github username:** ${submissionData.contact?.githubUsername ? `@${submissionData.contact?.githubUsername}` : "--"} **Submission hash (on-chain):** ${submissionData.submissionResult?.transactionHash} **Severity:** ${description.severity} diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionProject/SubmissionProject.tsx b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionProject/SubmissionProject.tsx index f1627b065..d8b444210 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionProject/SubmissionProject.tsx +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionProject/SubmissionProject.tsx @@ -9,7 +9,7 @@ import { StyledSubmissionProject } from "./styles"; export function SubmissionProject() { const { t } = useTranslation(); - const { submissionData, setSubmissionData } = useContext(SubmissionFormContext); + const { submissionData, setSubmissionData, allFormDisabled } = useContext(SubmissionFormContext); const [userInput, setUserInput] = useState(""); const { activeVaults } = useVaults(); @@ -36,7 +36,7 @@ export function SubmissionProject() { key={index} vault={vault} expanded={false} - onSelect={() => handleSelectedProject(vault)} + onSelect={!allFormDisabled ? () => handleSelectedProject(vault) : () => {}} selected={submissionData?.project?.projectId === vault.id} /> ); diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionProcessed/SubmissionProcessed.tsx b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionProcessed/SubmissionProcessed.tsx index 77d07de68..2e63a8561 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionProcessed/SubmissionProcessed.tsx +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionProcessed/SubmissionProcessed.tsx @@ -27,7 +27,7 @@ export function SubmissionProcessed() { const getSubmissionOnServerStatus = () => { let color = Colors.yellow; - let text = t("pending"); + let text = `${t("pending")}...`; switch (submissionStatus.server) { case SubmissionOpStatus.Success: diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/SubmissionReview.tsx b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/SubmissionReview.tsx index 347e75d31..05a6ac446 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/SubmissionReview.tsx +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/SubmissionReview.tsx @@ -1,4 +1,6 @@ +import MDEditor from "@uiw/react-md-editor"; import { Alert, Button, FormInput, Loading } from "components"; +import { disallowedElementsMarkdown } from "constants/constants"; import { SubmissionFormContext } from "pages/Submissions/SubmissionFormPage/store"; import { useContext } from "react"; import { useTranslation } from "react-i18next"; @@ -34,15 +36,27 @@ export function SubmissionReview() { />
- {/* + + */} + /> - + {/* */}
+ {/* {submissionData.ref === "audit-wizard" && ( + + )} */} diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/styles.ts b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/styles.ts index e5e132d48..3779669a5 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/styles.ts +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/FormSteps/SubmissionSubmit/components/SubmissionReview/styles.ts @@ -11,7 +11,9 @@ export const StyledSubmissionReview = styled.div` } .buttons { + margin-top: ${getSpacing(2)}; display: flex; + gap: ${getSpacing(4)}; justify-content: flex-end; } `; diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/SubmissionFormPage.tsx b/packages/web/src/pages/Submissions/SubmissionFormPage/SubmissionFormPage.tsx index d05495ee9..4ea212768 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/SubmissionFormPage.tsx +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/SubmissionFormPage.tsx @@ -1,11 +1,16 @@ -import { Seo } from "components"; +import { IVulnerabilitySeverity } from "@hats-finance/shared"; +import ErrorIcon from "@mui/icons-material/ErrorOutlineOutlined"; +import ClearIcon from "@mui/icons-material/HighlightOffOutlined"; +import { Button, Seo } from "components"; import { LocalStorage } from "constants/constants"; import { LogClaimContract } from "contracts"; import { useVaults } from "hooks/subgraph/vaults/useVaults"; +import useConfirm from "hooks/useConfirm"; import { calcCid } from "pages/Submissions/SubmissionFormPage/encrypt"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useSearchParams } from "react-router-dom"; +import { IS_PROD } from "settings"; import { getAppVersion } from "utils"; import { useNetwork, useWaitForTransaction } from "wagmi"; import { @@ -18,18 +23,27 @@ import { import SubmissionFormCard from "../SubmissionFormPage/SubmissionFormCard/SubmissionFormCard"; import { ISubmissionFormContext, SUBMISSION_INIT_DATA, SubmissionFormContext } from "./store"; import { StyledSubmissionFormPage } from "./styles"; -import { submitVulnerabilitySubmission } from "./submissionsService.api"; -import { ISubmissionData, SubmissionOpStatus, SubmissionStep } from "./types"; +import { submitVulnerabilitySubmission, verifyAuditWizardSignature } from "./submissionsService.api"; +import { + IAuditWizardSubmissionData, + ISubmissionData, + SubmissionOpStatus, + SubmissionStep, + getCurrentAuditwizardSubmission, +} from "./types"; export const SubmissionFormPage = () => { const { t } = useTranslation(); + const confirm = useConfirm(); const [searchParams] = useSearchParams(); const { chain } = useNetwork(); const [currentStep, setCurrentStep] = useState(); const [submissionData, setSubmissionData] = useState(); + const [allFormDisabled, setAllFormDisabled] = useState(false); + const [receivedSubmissionAuditwizard, setReceivedSubmissionAuditwizard] = useState(); - const { activeVaults } = useVaults(); + const { activeVaults, vaultsReadyAllChains } = useVaults(); const vault = (activeVaults ?? []).find((vault) => vault.id === submissionData?.project?.projectId); const steps = useMemo( @@ -81,6 +95,7 @@ export const SubmissionFormPage = () => { callReset(); setSubmissionData(SUBMISSION_INIT_DATA); setCurrentStep(0); + setAllFormDisabled(false); }; // Loads initial state of the vault @@ -111,6 +126,7 @@ export const SubmissionFormPage = () => { } else if (cachedData.version !== getAppVersion()) { setSubmissionData(SUBMISSION_INIT_DATA); } else { + if (cachedData.ref === "audit-wizard") setAllFormDisabled(true); setSubmissionData(cachedData); } } catch (e) { @@ -172,8 +188,132 @@ export const SubmissionFormPage = () => { const submission = submissionData?.submissionsDescriptions?.submission; const calculatedCid = await calcCid(submission); + if (submissionData.ref === "audit-wizard") { + if (!submissionData.auditWizardData) return; + // Verify if the submission was not changed and validate the signature + const auditwizardSubmission = getCurrentAuditwizardSubmission(submissionData.auditWizardData, submissionData); + + if (JSON.stringify(submissionData.auditWizardData) !== JSON.stringify(auditwizardSubmission)) { + return confirm({ + title: t("submissionChanged"), + titleIcon: , + description: t("submissionChangedExplanationAuditWizard"), + confirmText: t("gotIt"), + }); + } + + const res = await verifyAuditWizardSignature(auditwizardSubmission); + if (!res) { + return confirm({ + title: t("submissionNotValid"), + titleIcon: , + description: t("submissionNotValidExplanationAuditWizard"), + confirmText: t("gotIt"), + }); + } + } + sendVulnerabilityOnChain(calculatedCid); - }, [sendVulnerabilityOnChain, submissionData]); + }, [sendVulnerabilityOnChain, submissionData, confirm, t]); + + const handleClearSubmission = async () => { + const wantsToClear = await confirm({ + title: t("clearSubmission"), + titleIcon: , + description: t("clearSubmissionExplanation"), + cancelText: t("no"), + confirmText: t("clearForm"), + }); + + if (!wantsToClear) return; + reset(); + }; + + const populateDataFromAuditWizard = async (auditWizardSubmission: IAuditWizardSubmissionData) => { + if (!vaultsReadyAllChains) return; + + if (submissionData?.project?.projectId) { + const wantsToClear = await confirm({ + title: t("existingSubmission"), + titleIcon: , + description: t("clearExistingSubmissionAuditWizardExplanation"), + cancelText: t("no"), + confirmText: t("clearForm"), + }); + if (!wantsToClear) return; + } + + reset(); + + const vault = activeVaults && activeVaults.find((vault) => vault.id === auditWizardSubmission.project.projectId); + if (!vault || !vault.description) { + return confirm({ + title: t("projectNotAvailable"), + titleIcon: , + description: t("projectNotAvailableExplanation"), + confirmText: t("gotIt"), + }); + } + + setSubmissionData((prev) => ({ + ...prev!, + ref: "audit-wizard", + auditWizardData: auditWizardSubmission, + project: { + verified: true, + projectName: vault.description?.["project-metadata"].name!, + ...auditWizardSubmission.project, + }, + terms: { verified: false }, + contact: { verified: false, ...auditWizardSubmission.contact }, + submissionsDescriptions: { + verified: false, + submission: "", + submissionMessage: "", + descriptions: auditWizardSubmission.submissionsDescriptions.descriptions.map((desc: any) => { + const severity = (vault.description?.severities as IVulnerabilitySeverity[]).find( + (sev) => + desc.severity.toLowerCase()?.includes(sev.name.toLowerCase()) || + sev.name.toLowerCase()?.includes(desc.severity.toLowerCase()) + ); + return { + title: desc.title, + description: desc.description, + severity: severity?.name.toLowerCase() ?? desc.severity.toLowerCase(), + isEncrypted: !severity?.decryptSubmissions, + files: [], + }; + }), + }, + submissionResult: undefined, + })); + setAllFormDisabled(true); + }; + + useEffect(() => { + const checkEvent = (event: MessageEvent) => { + const host = new URL(event.origin).host; + if (IS_PROD && host !== "app.auditwizard.io") return; + if (!event.data.signature || !event.data.project || !event.data.contact) return; + + setReceivedSubmissionAuditwizard(event.data); + }; + + window.addEventListener("message", checkEvent); + + return () => { + window.removeEventListener("message", checkEvent); + }; + }, []); + + // Populate data from audit wizard once vaults are ready + useEffect(() => { + if (!vaultsReadyAllChains) return; + if (!receivedSubmissionAuditwizard) return; + + populateDataFromAuditWizard(receivedSubmissionAuditwizard); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [vaultsReadyAllChains, receivedSubmissionAuditwizard]); const context: ISubmissionFormContext = { reset, @@ -186,12 +326,28 @@ export const SubmissionFormPage = () => { sendSubmissionToServer, isSubmitting, isSigningSubmission, + allFormDisabled, }; return ( <> +
+ {submissionData?.ref === "audit-wizard" && ( +
+ audit wizard logo +

{t("Submissions.submissionSubmittedViaAuditWizard")}

+
+ )} + {submissionData?.project?.projectId && ( + + )} +
+
{steps.map((step, index) => ( diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/store.ts b/packages/web/src/pages/Submissions/SubmissionFormPage/store.ts index 9b9e63e56..b261a02d0 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/store.ts +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/store.ts @@ -16,6 +16,7 @@ export interface ISubmissionFormContext { submitSubmission: Function; sendSubmissionToServer: Function; reset: Function; + allFormDisabled: boolean; } export const SubmissionFormContext = createContext(undefined as any); diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/styles.ts b/packages/web/src/pages/Submissions/SubmissionFormPage/styles.ts index ab3024338..b478f0dbc 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/styles.ts +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/styles.ts @@ -1,4 +1,5 @@ import styled from "styled-components"; +import { getSpacing } from "styles"; export const StyledSubmissionFormPage = styled.div` position: relative; @@ -7,4 +8,30 @@ export const StyledSubmissionFormPage = styled.div` max-width: var(--element-max-width); margin: auto; } + + .top-controls { + display: flex; + align-items: center; + gap: ${getSpacing(1)}; + margin-bottom: ${getSpacing(4)}; + + .auditWizardSubmission { + display: flex; + flex-direction: column; + gap: ${getSpacing(1)}; + align-items: flex-start; + background: var(--background-3); + width: fit-content; + padding: ${getSpacing(1.5)} ${getSpacing(2)}; + border-radius: ${getSpacing(1)}; + + img { + height: ${getSpacing(3)}; + } + + p { + font-size: var(--xxsmall); + } + } + } `; diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/submissionsService.api.ts b/packages/web/src/pages/Submissions/SubmissionFormPage/submissionsService.api.ts index 898be8411..87579c9d3 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/submissionsService.api.ts +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/submissionsService.api.ts @@ -1,8 +1,9 @@ import { IVault } from "@hats-finance/shared"; import { axiosClient } from "config/axiosClient"; +import { auditWizardVerifyService } from "constants/constants"; import { BASE_SERVICE_URL } from "settings"; import { getGithubIssueDescription } from "../SubmissionFormPage/FormSteps/SubmissionDescriptions/utils"; -import { ISubmissionData, ISubmitSubmissionRequest } from "./types"; +import { IAuditWizardSubmissionData, ISubmissionData, ISubmitSubmissionRequest } from "./types"; /** * Submits a new vulnerability submission @@ -30,11 +31,11 @@ export async function submitVulnerabilitySubmission( createIssueRequests: vault.description?.["project-metadata"].type === "audit" ? submissionData.submissionsDescriptions.descriptions - .filter((desc) => !desc.isEncrypted) - .map((description) => ({ + ?.filter((desc) => !desc.isEncrypted) + ?.map((description) => ({ issueTitle: description.title, issueDescription: getGithubIssueDescription(submissionData, description), - issueFiles: description.files.map((file) => file.ipfsHash), + issueFiles: description.files?.map((file) => file.ipfsHash), })) : [], }; @@ -46,3 +47,15 @@ export async function submitVulnerabilitySubmission( return { success: false }; } } + +/** + * Verifies the signature sent by Audit Wizard + */ +export async function verifyAuditWizardSignature(auditWizardSubmission: IAuditWizardSubmissionData): Promise { + try { + const res = await axiosClient.put(`${auditWizardVerifyService}`, auditWizardSubmission); + return res.status === 200; + } catch (error) { + return false; + } +} diff --git a/packages/web/src/pages/Submissions/SubmissionFormPage/types.ts b/packages/web/src/pages/Submissions/SubmissionFormPage/types.ts index 28c70bcd1..05a0855b2 100644 --- a/packages/web/src/pages/Submissions/SubmissionFormPage/types.ts +++ b/packages/web/src/pages/Submissions/SubmissionFormPage/types.ts @@ -32,7 +32,7 @@ export interface ISubmissionsDescriptionsData { description: string; severity: string; files: ISavedFile[]; - sessionKey: SessionKey; + sessionKey?: SessionKey; isEncrypted?: boolean; }[]; } @@ -51,12 +51,14 @@ export interface ISubmissionResultData { } export interface ISubmissionData { - version: string; + version?: string; project?: ISubmissionProjectData; contact?: ISubmissionContactData; submissionsDescriptions: ISubmissionsDescriptionsData; terms?: ISubmissionTermsData; submissionResult?: ISubmissionResultData; + ref?: "audit-wizard"; + auditWizardData?: IAuditWizardSubmissionData; } export enum SubmissionOpStatus { @@ -79,3 +81,47 @@ export interface ISubmitSubmissionRequest { issueFiles: string[]; }[]; } + +export interface IAuditWizardSubmissionData { + signature: string; + contact: { + beneficiary: string; + communicationChannel: string; + communicationChannelType: ISubmissionContactData["communicationChannelType"]; + }; + project: { projectId: string }; + submissionsDescriptions: { descriptions: { title: string; severity: string; description: string }[] }; +} + +/** + * This functions puts the current state of the form into the AuditWizard format in order to + * verify it with their API. The from should have exactly the same values as the received from + * audit wizard. + */ +export const getCurrentAuditwizardSubmission = ( + awSubmission: IAuditWizardSubmissionData, + form: ISubmissionData +): IAuditWizardSubmissionData => { + return { + ...awSubmission, + contact: { + ...awSubmission.contact, + beneficiary: form.contact?.beneficiary ?? "", + communicationChannel: form.contact?.communicationChannel ?? "", + communicationChannelType: form.contact?.communicationChannelType ?? "email", + }, + project: { + ...awSubmission.project, + projectId: form.project?.projectId ?? "", + }, + submissionsDescriptions: { + ...awSubmission.submissionsDescriptions, + descriptions: + form.submissionsDescriptions?.descriptions.map((d, idx) => ({ + title: d.title, + description: d.description, + severity: awSubmission.submissionsDescriptions.descriptions[idx].severity, + })) ?? [], + }, + }; +};