Skip to content

Commit

Permalink
ui-only WIP on redoing how action items are reported and the outcome …
Browse files Browse the repository at this point in the history
…of a threat model
  • Loading branch information
Tethik committed Oct 23, 2023
1 parent be2ad22 commit 76fff45
Show file tree
Hide file tree
Showing 18 changed files with 375 additions and 264 deletions.
60 changes: 60 additions & 0 deletions app/src/components/elements/CollapsePaper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
KeyboardArrowDownRounded,
KeyboardArrowUpRounded,
} from "@mui/icons-material";
import { Badge, Box, Collapse, IconButton, Paper } from "@mui/material";
import { useState } from "react";

export function CollapsePaper({
title,
count,
children,
defaultExpanded = false,
sx,
}) {
const [expanded, setExpanded] = useState(defaultExpanded);

return (
<Paper elevation={16} sx={sx}>
<Box
display="flex"
alignItems="center"
sx={{ paddingLeft: "10px", "&:hover": { cursor: "pointer" } }}
onClick={(e) => {
if (e.target === e.currentTarget) {
setExpanded(!expanded);
}
}}
>
<Badge
badgeContent={count}
onClick={() => setExpanded(!expanded)}
sx={{
alignItems: "center",
gap: "10px",
"& span": {
position: "relative",
transform: "scale(1)",
backgroundColor: "dimgray",
},
}}
>
{title}
</Badge>
<IconButton
disableRipple
size="large"
sx={{
marginLeft: "auto",
}}
onClick={() => setExpanded(!expanded)}
>
{expanded ? <KeyboardArrowUpRounded /> : <KeyboardArrowDownRounded />}
</IconButton>
</Box>
<Collapse in={expanded} timeout="auto" unmountOnExit>
{children}
</Collapse>
</Paper>
);
}
12 changes: 9 additions & 3 deletions app/src/components/elements/ColorSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ const baseMarks = [
},
];

export function ColorSlider({ marks, defaultValue, onChange }) {
export function ColorSlider({
marks,
defaultValue,
onChange,
hideDescription,
}) {
const joinedMarks = marks.map((m, i) => ({
...(baseMarks.length > i ? baseMarks[i] : {}),
...m,
Expand All @@ -43,7 +48,8 @@ export function ColorSlider({ marks, defaultValue, onChange }) {
marks={joinedMarks}
min={0}
max={4}
valueLabelDisplay="off"
// valueLabelFormat={(v, i) => joinedMarks[i].label}
// valueLabelDisplay="on"
onChange={(e) => {
setSelectedMark(
joinedMarks.find((m) => m.value === e.target.value)
Expand All @@ -53,7 +59,7 @@ export function ColorSlider({ marks, defaultValue, onChange }) {
sx={{ color: selectedMark?.color || "primary" }}
/>
</Box>
{selectedMark?.description && (
{!hideDescription && selectedMark?.description && (
<Typography
sx={{ "white-space": "pre-wrap" }}
variant="caption"
Expand Down
42 changes: 42 additions & 0 deletions app/src/components/elements/UserChip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Chip, IconButton } from "@mui/material";
import EmailIcon from "@mui/icons-material/Email";
import ChatIcon from "@mui/icons-material/Chat";

export function UserChip({ user }) {
return (
<Chip
size="small"
sx={{
color: (theme) => theme.palette.review.text,
}}
variant="outlined"
label={user.name}
icon={
<>
{user?.slackUrl && (
<IconButton
href={user?.slackUrl}
target="_blank"
rel="noreferrer"
color="inherit"
size="small"
>
<ChatIcon />
</IconButton>
)}
{user?.mail && (
<IconButton
href={`mailto:${user?.mail}`}
target="_blank"
rel="noreferrer"
color="inherit"
size="small"
>
<EmailIcon />
</IconButton>
)}
</>
}
/>
);
}
2 changes: 0 additions & 2 deletions app/src/components/elements/modal/ModalManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { ChangeReviewer } from "../../model/modals/ChangeReviewer";
import { Tutorial } from "../../model/tutorial/Tutorial";
import { CancelReview } from "../../reviews/modals/CancelReview";
import { DeclineReview } from "../../reviews/modals/DeclineReview";
import { ViewActionItems } from "../../model/modals/ViewActionItems";

export const MODALS = {
ChangeReviewer,
Expand All @@ -23,7 +22,6 @@ export const MODALS = {
DeleteSelected,
DeclineReview,
CancelReview,
ViewActionItems,
};

export function ModalManager() {
Expand Down
22 changes: 22 additions & 0 deletions app/src/components/model/hooks/useActionItems.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useListThreatsQuery } from "../../../api/gram/threats";
import { useModelID } from "./useModelID";

export function useActionItems() {
const modelId = useModelID();
const { data: threats } = useListThreatsQuery({ modelId });

const actionItems = threats?.threats
? Object.keys(threats?.threats)
.map((componentId) => ({
componentId,
threats: threats?.threats[componentId].filter(
(th) => th.isActionItem
),
}))
.filter(({ threats }) => threats && threats.length > 0)
: [];

console.log(actionItems);

return actionItems;
}
118 changes: 4 additions & 114 deletions app/src/components/model/modals/ApproveReview.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,89 +20,11 @@ import {
useGetReviewQuery,
} from "../../../api/gram/review";
import { modalActions } from "../../../redux/modalSlice";
import { ColorSlider } from "../../elements/ColorSlider";
import { LoadingPage } from "../../elements/loading/loading-page/LoadingPage";
import { PERMISSIONS } from "../constants";
import { ActionItemList } from "./ActionItemList";

function LikelihoodSlider({ onChange }) {
const marks = [
{
label: "Rare",
description: `➢ This will probably never happen/recur
➢ Every 25 years`,
},
{
label: "Unlikely",
description: `➢ This is not likely to happen/recur but could
➢ Every 10 years`,
},
{
label: "Occasional",
description: `➢ This is unexpected to happen/recur but is certainly possible to occur
➢ Every 5 years`,
},
{
label: "Likely",
description: `➢ This will probably happen/recur but is not a persisting issue.
➢ Every 3 years`,
},
{
label: "Almost certain",
description: `➢ This will undoubtedly happen/recur
➢ Every year`,
},
];

return (
<>
<ColorSlider
defaultValue={1}
marks={marks}
onChange={(e) => onChange(marks[e.target.value])}
/>
</>
);
}

function ImpactSlider({ onChange }) {
const marks = [
{
label: "Very low",
description: `➢ Users can not interact with the service <1h
➢ No regulatory sanctions/fines`,
},
{
label: "Low",
description: `➢ Users can not interact with the service <1-4h
➢ Incident reviewed by authorities but dismissed`,
},
{
label: "Medium",
description: `➢ Users can not interact with the service <4-10h
➢ Incident reviewed by authorities and regulatory warning`,
},
{
label: "High",
description: `➢ Users can not interact with the service <10-16h
➢ Incident reviewed by authorities and sanctions/fines imposed`,
},
{
label: "Very high",
description: `➢ Users can not interact with the service >16h
➢ Incident reviewed by authorities and sanctions/fines threaten operations / Loss of licence`,
},
];

return (
<ColorSlider
defaultValue={1}
step={null} // restricts to only these steps
marks={marks}
onChange={(e) => onChange(marks[e.target.value])}
/>
);
}
import { ActionItemList } from "../panels/left/ActionItemList";
import { ImpactSlider } from "./ImpactSlider";
import { LikelihoodSlider } from "./LikelihoodSlider";

export function ApproveReview({ modelId }) {
const dispatch = useDispatch();
Expand Down Expand Up @@ -145,40 +67,8 @@ export function ApproveReview({ modelId }) {
<DialogContent sx={{ paddingTop: "0" }}>
{(isUninitialized || isLoading) && (
<>
<Divider textAlign="left">Risk Evaluation</Divider>
{/* <DialogContentText>
Every system threat model is connected to a risk ticket. When you
approve this threat model, it will be automatically created for
you (if the model is connected to a system).
</DialogContentText> */}
{/* <br /> */}
<DialogContentText>
Based on the threat model, set the risk value as your estimate of
the overall risk of all threats/controls found in the threat
model.
</DialogContentText>

<br />

<Typography>Impact</Typography>
<ImpactSlider
onChange={(value) =>
setExtras({ ...extras, impact: value.label })
}
/>

<br />

<Typography>Likelihood</Typography>
<LikelihoodSlider
onChange={(value) =>
setExtras({ ...extras, likelihood: value.label })
}
/>

<br />
<Divider textAlign="left">Action Items</Divider>
<ActionItemList />
<ActionItemList automaticallyExpanded={true} />

<br />
<Divider textAlign="left">Summary</Divider>
Expand Down
41 changes: 41 additions & 0 deletions app/src/components/model/modals/ImpactSlider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ColorSlider } from "../../elements/ColorSlider";

export function ImpactSlider({ onChange, ...props }) {
const marks = [
{
label: "Very low",
description: `➢ Users can not interact with the service <1h
➢ No regulatory sanctions/fines`,
},
{
label: "Low",
description: `➢ Users can not interact with the service <1-4h
➢ Incident reviewed by authorities but dismissed`,
},
{
label: "Medium",
description: `➢ Users can not interact with the service <4-10h
➢ Incident reviewed by authorities and regulatory warning`,
},
{
label: "High",
description: `➢ Users can not interact with the service <10-16h
➢ Incident reviewed by authorities and sanctions/fines imposed`,
},
{
label: "Very high",
description: `➢ Users can not interact with the service >16h
➢ Incident reviewed by authorities and sanctions/fines threaten operations / Loss of licence`,
},
];

return (
<ColorSlider
defaultValue={1}
step={null} // restricts to only these steps
marks={marks}
onChange={(e) => onChange(marks[e.target.value])}
{...props}
/>
);
}
40 changes: 40 additions & 0 deletions app/src/components/model/modals/LikelihoodSlider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ColorSlider } from "../../elements/ColorSlider";

export function LikelihoodSlider({ onChange, ...props }) {
const marks = [
{
label: "Rare",
description: `➢ This will probably never happen/recur
➢ Every 25 years`,
},
{
label: "Unlikely",
description: `➢ This is not likely to happen/recur but could
➢ Every 10 years`,
},
{
label: "Occasional",
description: `➢ This is unexpected to happen/recur but is certainly possible to occur
➢ Every 5 years`,
},
{
label: "Likely",
description: `➢ This will probably happen/recur but is not a persisting issue.
➢ Every 3 years`,
},
{
label: "Almost certain",
description: `➢ This will undoubtedly happen/recur
➢ Every year`,
},
];

return (
<ColorSlider
defaultValue={1}
marks={marks}
onChange={(e) => onChange(marks[e.target.value])}
{...props}
/>
);
}
Loading

0 comments on commit 76fff45

Please sign in to comment.