Skip to content

Commit

Permalink
Build basic UI for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
john681611 committed Aug 14, 2023
1 parent a51591c commit 246a05a
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 69 deletions.
129 changes: 70 additions & 59 deletions application/database/db.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from neo4j import GraphDatabase
import neo4j
from sqlalchemy.orm import aliased
Expand Down Expand Up @@ -159,90 +158,100 @@ class Embeddings(BaseModel): # type: ignore
)



class NEO_DB:
__instance = None

driver = None
connected = False

@classmethod
def instance(self):
if self.__instance is None:
self.__instance = self.__new__(self)

URI = os.getenv('NEO4J_URI') or "neo4j://localhost:7687"
AUTH = (os.getenv('NEO4J_USR') or "neo4j", os.getenv('NEO4J_PASS') or "password")
URI = os.getenv("NEO4J_URI") or "neo4j://localhost:7687"
AUTH = (
os.getenv("NEO4J_USR") or "neo4j",
os.getenv("NEO4J_PASS") or "password",
)
self.driver = GraphDatabase.driver(URI, auth=AUTH)

try:
self.driver.verify_connectivity()
self.connected = True
except neo4j.exceptions.ServiceUnavailable:
logger.error("NEO4J ServiceUnavailable error - disabling neo4j related features")

except neo4j.exceptions.ServiceUnavailable:
logger.error(
"NEO4J ServiceUnavailable error - disabling neo4j related features"
)

return self.__instance

def __init__(sel):
raise ValueError("NEO_DB is a singleton, please call instance() instead")

@classmethod
def add_cre(self, dbcre: CRE):
if not self.connected:
if not self.connected:
return
self.driver.execute_query(
"MERGE (n:CRE {id: $nid, name: $name, description: $description, external_id: $external_id})",
nid=dbcre.id,
name=dbcre.name,
description=dbcre.description,
external_id=dbcre.external_id,
database_="neo4j")

self.driver.execute_query(
"MERGE (n:CRE {id: $nid, name: $name, description: $description, external_id: $external_id})",
nid=dbcre.id,
name=dbcre.name,
description=dbcre.description,
external_id=dbcre.external_id,
database_="neo4j",
)

@classmethod
def add_dbnode(self, dbnode: Node):
if not self.connected:
return
self.driver.execute_query(
"MERGE (n:Node {id: $nid, name: $name, section: $section, section_id: $section_id, subsection: $subsection, tags: $tags, version: $version, description: $description, ntype: $ntype})",
nid=dbnode.id,
name=dbnode.name,
section=dbnode.section,
section_id=dbnode.section_id,
subsection=dbnode.subsection or "",
tags=dbnode.tags,
version=dbnode.version or "",
description=dbnode.description,
ntype=dbnode.ntype,
database_="neo4j")

"MERGE (n:Node {id: $nid, name: $name, section: $section, section_id: $section_id, subsection: $subsection, tags: $tags, version: $version, description: $description, ntype: $ntype})",
nid=dbnode.id,
name=dbnode.name,
section=dbnode.section,
section_id=dbnode.section_id,
subsection=dbnode.subsection or "",
tags=dbnode.tags,
version=dbnode.version or "",
description=dbnode.description,
ntype=dbnode.ntype,
database_="neo4j",
)

@classmethod
def link_CRE_to_CRE(self, id1, id2, link_type):
if not self.connected:
return
self.driver.execute_query(
"MATCH (a:CRE), (b:CRE) "
"WHERE a.id = $aID AND b.id = $bID "
"CALL apoc.create.relationship(a,$relType, {},b) "
"YIELD rel "
"RETURN rel",
aID=id1,
bID=id2,
relType=str.upper(link_type).replace(' ', '_'),
database_="neo4j")

"MATCH (a:CRE), (b:CRE) "
"WHERE a.id = $aID AND b.id = $bID "
"CALL apoc.create.relationship(a,$relType, {},b) "
"YIELD rel "
"RETURN rel",
aID=id1,
bID=id2,
relType=str.upper(link_type).replace(" ", "_"),
database_="neo4j",
)

@classmethod
def link_CRE_to_Node(self, CRE_id, node_id, link_type):
if not self.connected:
return
self.driver.execute_query(
"MATCH (a:CRE), (b:Node) "
"WHERE a.id = $aID AND b.id = $bID "
"CALL apoc.create.relationship(a,$relType, {},b) "
"YIELD rel "
"RETURN rel",
aID=CRE_id,
bID=node_id,
relType=str.upper(link_type).replace(' ', '_'),
database_="neo4j")
"MATCH (a:CRE), (b:Node) "
"WHERE a.id = $aID AND b.id = $bID "
"CALL apoc.create.relationship(a,$relType, {},b) "
"YIELD rel "
"RETURN rel",
aID=CRE_id,
bID=node_id,
relType=str.upper(link_type).replace(" ", "_"),
database_="neo4j",
)

@classmethod
def gap_analysis(self, name_1, name_2):
if not self.connected:
Expand All @@ -256,28 +265,28 @@ def gap_analysis(self, name_1, name_2):
"RETURN p ",
name1=name_1,
name2=name_2,
database_="neo4j"
database_="neo4j",
)

def format_segment(seg):
return {
return {
"start": {
"name": seg.start_node["name"],
"sectionID": seg.start_node["section_id"],
"section": seg.start_node["section"],
"subsection": seg.start_node["subsection"],
"description": seg.start_node["description"],
"id": seg.start_node["id"]
"id": seg.start_node["id"],
},
"end": {
"name": seg.end_node["name"],
"sectionID": seg.end_node["section_id"],
"section": seg.end_node["section"],
"subsection": seg.end_node["subsection"],
"description": seg.end_node["description"],
"id": seg.end_node["id"]
"id": seg.end_node["id"],
},
"relationship": seg.type
"relationship": seg.type,
}

def format_record(rec):
Expand All @@ -288,19 +297,21 @@ def format_record(rec):
"section": rec.start_node["section"],
"subsection": rec.start_node["subsection"],
"description": rec.start_node["description"],
"id": rec.start_node["id"]
"id": rec.start_node["id"],
},
"end": {
"name": rec.end_node["name"],
"sectionID": rec.end_node["section_id"],
"section": rec.end_node["section"],
"subsection": rec.end_node["subsection"],
"description": rec.end_node["description"],
"id": rec.end_node["id"]
"id": rec.end_node["id"],
},
"path": [format_segment(seg) for seg in rec.relationships]
"path": [format_segment(seg) for seg in rec.relationships],
}
return [format_record(rec['p']) for rec in records]

return [format_record(rec["p"]) for rec in records]


class CRE_Graph:
graph: nx.Graph = None
Expand Down Expand Up @@ -339,7 +350,7 @@ def add_cre(cls, dbcre: CRE, graph: nx.DiGraph) -> nx.DiGraph:
def add_dbnode(cls, dbnode: Node, graph: nx.DiGraph) -> nx.DiGraph:
if dbnode:
cls.neo_db.add_dbnode(dbnode)
# coma separated tags
# coma separated tags

graph.add_node(
"Node: " + str(dbnode.id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export const DocumentNode: FunctionComponent<DocumentNode> = ({
let lastDocumentName = sortedResults[0].document.name;
return (
<div className="document-node__link-type-container" key={type}>
{idx > 0 && <hr style={{borderColor: "transparent", margin: "20px 0"}} />}
{idx > 0 && <hr style={{ borderColor: 'transparent', margin: '20px 0' }} />}
<div>
<b>Which {getDocumentTypeText(type, links[0].document.doctype, node.doctype)}</b>:
{/* Risk here of mixed doctype in here causing odd output */}
Expand Down
1 change: 1 addition & 0 deletions application/frontend/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export const CRE = '/cre';
export const GRAPH = '/graph';
export const DEEPLINK = '/deeplink';
export const BROWSEROOT = '/root_cres';
export const GAP_ANALYSIS = '/gap_analysis';
133 changes: 133 additions & 0 deletions application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
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<string>();
const [CompareStandard, setCompareStandard] = useState<string>();
const [gapAnalysis, setGapAnalysis] = useState<string>();
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 (
<div>
<Dropdown
placeholder="Base Standard"
search
selection
options={standardOptions}
onChange={(e, { value }) => setBaseStandard(value?.toString())}
/>
<Dropdown
placeholder="Compare Standard"
search
selection
options={standardOptions}
onChange={(e, { value }) => setCompareStandard(value?.toString())}
/>
{gapAnalysis && (
<Table celled padded>
<Table.Header>
<Table.Row>
<Table.HeaderCell>{BaseStandard}</Table.HeaderCell>
<Table.HeaderCell>{CompareStandard}</Table.HeaderCell>
</Table.Row>
</Table.Header>

<Table.Body>
{Object.keys(gapAnalysis).map((key) => (
<Table.Row>
<Table.Cell>
<Label ribbon>
{gapAnalysis[key].start.name} {gapAnalysis[key].start.sectionID}{' '}
{gapAnalysis[key].start.section} {gapAnalysis[key].start.subsection}{' '}
{gapAnalysis[key].start.description} {gapAnalysis[key].start.id}
</Label>
</Table.Cell>
<Table.Cell>
{gapAnalysis[key].paths.map((path) => {
let segmentID = gapAnalysis[key].start.id;
return (
<Popup
wide="very"
hoverable
content={path.path
.map((segment) => {
const { text, nextID } = GetSegmentText(segment, segmentID);
segmentID = nextID;
return text;
})
.join('')}
trigger={
<span>
{path.end.name} {path.end.sectionID} {path.end.section} {path.end.subsection}{' '}
{path.end.description},{' '}
</span>
}
/>
);
})}
<br />({gapAnalysis[key].paths.length})
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import './MembershipRequired.scss';
import React from 'react';
import { Button, Header } from 'semantic-ui-react';


export const MembershipRequired = () => {
return (
<div className="membership-required">
Expand All @@ -12,7 +11,7 @@ export const MembershipRequired = () => {
</Header>
<p>A OWASP Membership account is needed to login</p>
<Button primary href="https://owasp.org/membership/">
Sign up
Sign up
</Button>
</div>
);
Expand Down
Loading

0 comments on commit 246a05a

Please sign in to comment.