diff --git a/application/frontend/src/const.ts b/application/frontend/src/const.ts index 340c5d7f4..bbc982412 100644 --- a/application/frontend/src/const.ts +++ b/application/frontend/src/const.ts @@ -34,3 +34,4 @@ export const CRE = '/cre'; export const GRAPH = '/graph'; export const DEEPLINK = '/deeplink'; export const BROWSEROOT = '/root_cres'; +export const GAP_ANALYSIS= '/gap_analysis' diff --git a/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx b/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx new file mode 100644 index 000000000..4e8979fd3 --- /dev/null +++ b/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx @@ -0,0 +1,108 @@ +import React, { useEffect, useState } from "react" +import { Dropdown, Label, Popup, Segment, Table } from "semantic-ui-react" +import { useEnvironment } from '../../hooks'; + + +const GetSegmentText = (segment, segmentID) => { + let textPart = segment.end + let nextID = segment.end.id + let arrow = '->' + if(segmentID !== segment.start.id) + { + textPart = segment.start + nextID = segment.start.id + arrow = '<-' + } + const text = `${arrow} ${segment.relationship} ${arrow} ${textPart.name} ${textPart.sectionID} ${textPart.section} ${textPart.subsection} ${textPart.description}` + return {text, nextID} +} + +export const GapAnalysis = () => { + const standardOptions = [ + { key: "", text: "", value: undefined, }, + { key: "OWASP Top 10 2021", text: "OWASP Top 10 2021", value: "OWASP Top 10 2021", }, + { key: "NIST 800-53 v5", text: "NIST 800-53 v5", value: "NIST 800-53 v5", }, + { key: "ISO 27001", text: "ISO 27001", value: "ISO 27001", }, + { key: "Cloud Controls Matrix", text: "Cloud Controls Matrix", value: "Cloud Controls Matrix", }, + { key: "ASVS", text: "ASVS", value: "ASVS", }, + { key: "OWASP Proactive Controls", text: "OWASP Proactive Controls", value: "OWASP Proactive Controls", }, + { key: "SAMM", text: "SAMM", value: "SAMM", }, + { key: "CWE", text: "CWE", value: "CWE", }, + { key: "OWASP Cheat Sheets", text: "OWASP Cheat Sheets", value: "OWASP Cheat Sheets", }, + { key: "OWASP Web Security Testing Guide (WSTG)", text: "OWASP Web Security Testing Guide (WSTG)", value: "OWASP Web Security Testing Guide (WSTG)", }, + { key: "NIST 800-63", text: "NIST 800-63", value: "NIST 800-63", }, + { key: "Cheat_sheets", text: "Cheat_sheets", value: "Cheat_sheets", }, + { key: "CAPEC", text: "CAPEC", value: "CAPEC", }, + { key: "ZAP Rule", text: "ZAP Rule", value: "ZAP Rule", }, + { key: "OWASP", text: "OWASP", value: "OWASP", }, + { key: "OWASP Secure Headers Project", text: "OWASP Secure Headers Project", value: "OWASP Secure Headers Project", }, + { key: "PCI DSS", text: "PCI DSS", value: "PCI DSS", }, + { key: "OWASP Juice Shop", text: "OWASP Juice Shop", value: "OWASP Juice Shop" }, + ] + const [BaseStandard, setBaseStandard] = useState(); + const [CompareStandard, setCompareStandard] = useState(); + const [gapAnalysis, setGapAnalysis] = useState(); + const { apiUrl } = useEnvironment(); + useEffect(() => { + const fetchData = async () => { + const result = await fetch(`${apiUrl}/gap_analysis?standard=${BaseStandard}&standard=${CompareStandard}`) + const resultObj = await result.json() + setGapAnalysis(resultObj) + } + + if (!BaseStandard || !CompareStandard || BaseStandard === CompareStandard) return + fetchData().catch(console.error) + }, [BaseStandard, CompareStandard, setGapAnalysis]); + + return (
+ setBaseStandard(value?.toString())} + /> + setCompareStandard(value?.toString())} + /> + {gapAnalysis && ( + + + + {BaseStandard} + {CompareStandard} + + + + + {Object.keys(gapAnalysis).map(key => ( + + + + + {gapAnalysis[key].paths.map(path => { + let segmentID = gapAnalysis[key].start.id + return ( + { + const {text, nextID} = GetSegmentText(segment, segmentID); + segmentID = nextID + return text }).join("")} + trigger={{path.end.name} {path.end.sectionID} {path.end.section} {path.end.subsection} {path.end.description}, } + /> + )})}
({gapAnalysis[key].paths.length})
+
+ ))} +
+
+ )} + +
) +} + diff --git a/application/frontend/src/routes.tsx b/application/frontend/src/routes.tsx index b71ac96fd..9e4d21e7d 100644 --- a/application/frontend/src/routes.tsx +++ b/application/frontend/src/routes.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; -import { BROWSEROOT, CRE, DEEPLINK, GRAPH, INDEX, SEARCH, SECTION, SECTION_ID, STANDARD } from './const'; +import { BROWSEROOT, CRE, DEEPLINK, GRAPH, INDEX, SEARCH, SECTION, SECTION_ID, STANDARD, GAP_ANALYSIS } from './const'; import { CommonRequirementEnumeration, Graph, Search, Standard } from './pages'; import { BrowseRootCres } from './pages/BrowseRootCres/browseRootCres'; import { Chatbot } from './pages/chatbot/chatbot'; @@ -8,6 +8,7 @@ import { Deeplink } from './pages/Deeplink/Deeplink'; import { SearchName } from './pages/Search/SearchName'; import { StandardSection } from './pages/Standard/StandardSection'; import { MembershipRequired } from './pages/MembershipRequired/MembershipRequired' +import { GapAnalysis } from './pages/GapAnalysis/GapAnalysis' export interface IRoute { path: string; @@ -23,6 +24,12 @@ export const ROUTES: IRoute[] = [ showFilter: false, showHeader: false, }, + { + path: GAP_ANALYSIS, + component: GapAnalysis, + showHeader: true, + showFilter: false, + }, { path: `/node${STANDARD}/:id${SECTION}/:section`, component: StandardSection,