From d833f70af069c980c8052220b02c2dcdb42874ab Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 9 Oct 2024 15:37:16 -0400 Subject: [PATCH 01/14] Replaced "info" icon in User Logs --- frontend/src/components/Logs/Logs.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Logs/Logs.tsx b/frontend/src/components/Logs/Logs.tsx index 57278e2e..132634a1 100644 --- a/frontend/src/components/Logs/Logs.tsx +++ b/frontend/src/components/Logs/Logs.tsx @@ -2,10 +2,10 @@ import { Dialog, DialogContent, DialogTitle, - Icon, IconButton, Paper } from '@mui/material'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Box } from '@mui/system'; import { DataGrid, @@ -149,7 +149,7 @@ export const Logs: FC = () => { setDialogDetails(cellValues.row); }} > - info + ); } From ef294313895f5f46609b1d42a69568743ba1b233 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 16 Oct 2024 13:44:42 -0400 Subject: [PATCH 02/14] Filtered out/replaced HTML encoding in Org Search - Filtered out/replaced HTML encoding in Org Search Autocomplete component. - Added a new decodeHTML utility function that replaces common HTML entities with their respective characters. - Run sortedOrgs through decodeHTML before updating the state used to render the list of organizations in the Autocomplete component. - This utility function can be updated with additional HTML entities as needed. --- .../RegionAndOrganizationFilters.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frontend/src/components/RegionAndOrganizationFilters.tsx b/frontend/src/components/RegionAndOrganizationFilters.tsx index ca596dbf..8643d366 100644 --- a/frontend/src/components/RegionAndOrganizationFilters.tsx +++ b/frontend/src/components/RegionAndOrganizationFilters.tsx @@ -124,6 +124,25 @@ export const RegionAndOrganizationFilters: React.FC< const sortedOrgs = filteredOrgs.sort((a, b) => a.name.localeCompare(b.name) ); + + // Utility function to replce HTML encodings + const decodeHtml = (orgName: string): string => { + const encodings: { [key: string]: string } = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + return orgName.replace(/&|<|>|"|'/g, (m) => { + return encodings[m]; + }); + }; + // Decode HTML encodings in org names + sortedOrgs.forEach((org) => { + org.name = decodeHtml(org.name); + }); + setOrgResults(sortedOrgs); } catch (e) { console.log(e); From e1cea8af95f99e67e0f69cec6798f1e4adf150e0 Mon Sep 17 00:00:00 2001 From: Janson Bunce Date: Thu, 17 Oct 2024 08:35:40 -0700 Subject: [PATCH 03/14] change default filters to only include regional admin --- frontend/src/hooks/useUserTypeFilters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/hooks/useUserTypeFilters.ts b/frontend/src/hooks/useUserTypeFilters.ts index b91d7364..5625052b 100644 --- a/frontend/src/hooks/useUserTypeFilters.ts +++ b/frontend/src/hooks/useUserTypeFilters.ts @@ -75,7 +75,7 @@ export const useUserTypeFilters: UseUserTypeFilters = ( }, { field: 'organizationId', - values: userOrgs, + values: [], type: 'any' } ]; From 292eb005b86d3dfaa7be37bcc459c9b65f216fd4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 18 Oct 2024 11:17:12 -0400 Subject: [PATCH 04/14] Co-authored-by: Janson Bunce --- backend/src/api/app.ts | 6 ++++++ frontend/scripts/api.js | 6 ++++++ frontend/scripts/docs.js | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/backend/src/api/app.ts b/backend/src/api/app.ts index 45e761da..b15215a2 100644 --- a/backend/src/api/app.ts +++ b/backend/src/api/app.ts @@ -141,6 +141,12 @@ app.use( }) ); +//Middleware to set Cache-Control headers +app.use((req, res, next) => { + res.setHeader('Cache-Control', 'private, must-revalidate, max-age=60'); + next(); +}); + app.use((req, res, next) => { res.setHeader('X-XSS-Protection', '0'); // Okta header diff --git a/frontend/scripts/api.js b/frontend/scripts/api.js index 57f6c411..f6f2bca5 100644 --- a/frontend/scripts/api.js +++ b/frontend/scripts/api.js @@ -65,6 +65,12 @@ app.use( }) ); +//Middleware to set Cache-Control headers +app.use((req, res, next) => { + res.setHeader('Cache-Control', 'private, max-age=3600'); + next(); +}); + app.use((req, res, next) => { res.setHeader('X-XSS-Protection', '0'); next(); diff --git a/frontend/scripts/docs.js b/frontend/scripts/docs.js index 1858db31..4c9dc283 100644 --- a/frontend/scripts/docs.js +++ b/frontend/scripts/docs.js @@ -38,6 +38,12 @@ app.use( }) ); +//Middleware to set Cache-Control headers +app.use((req, res, next) => { + res.setHeader('Cache-Control', 'private, max-age=3600'); + next(); +}); + app.use((req, res, next) => { res.setHeader('X-XSS-Protection', '0'); next(); From 53edf164d7b1a2d9daa64d88e5ef2e350ec4c100 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 18 Oct 2024 11:28:10 -0400 Subject: [PATCH 05/14] Fixed max-age typo - 60 to 3600 seconds --- backend/src/api/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/app.ts b/backend/src/api/app.ts index b15215a2..e9ea583a 100644 --- a/backend/src/api/app.ts +++ b/backend/src/api/app.ts @@ -143,7 +143,7 @@ app.use( //Middleware to set Cache-Control headers app.use((req, res, next) => { - res.setHeader('Cache-Control', 'private, must-revalidate, max-age=60'); + res.setHeader('Cache-Control', 'private, must-revalidate, max-age=3600'); next(); }); From af2cc542792dca5c0a6e04215c102f0749a9fadf Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 21 Oct 2024 15:55:14 -0400 Subject: [PATCH 06/14] Added aria-live region to filter tags in Inventory - Added aria-live region to Chip component in FilterTags.tsx - Removed 'strong' tag from Chip label to prevent screen reader from reading it as a separate entity, resulting in the reading of the label twice. --- frontend/src/pages/Search/FilterTags.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/Search/FilterTags.tsx b/frontend/src/pages/Search/FilterTags.tsx index 72afea18..4fcea86a 100644 --- a/frontend/src/pages/Search/FilterTags.tsx +++ b/frontend/src/pages/Search/FilterTags.tsx @@ -159,18 +159,14 @@ export const FilterTags: React.FC = ({ filters, removeFilter }) => { }, [filters]); return ( - + {filtersByColumn.map((filter, idx) => ( - {filter.label}: {filter.value} - - } + label={`${filter.label}: ${filter.value}`} onDelete={() => { if (filter.onClear) { console.log('custom clear'); From 06947d7390449e2a25e773fb0f7b126e0c1b0935 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 22 Oct 2024 13:59:30 -0400 Subject: [PATCH 07/14] Added FilterTags to Overview page - Adding the aria-live enabled FilterTags component allows the user to know when the Overview page is updated by Region and Organization filters. This is important for users who rely on screen readers to navigate the page. --- frontend/src/pages/Risk/Risk.tsx | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/Risk/Risk.tsx b/frontend/src/pages/Risk/Risk.tsx index 2e7334c4..7950bb24 100644 --- a/frontend/src/pages/Risk/Risk.tsx +++ b/frontend/src/pages/Risk/Risk.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useState, useEffect, useMemo } from 'react'; import classes from './Risk.module.scss'; -import { Card, CardContent, Grid, Paper, Typography } from '@mui/material'; +import { Box, Card, CardContent, Grid, Paper, Typography } from '@mui/material'; import VulnerabilityCard from './VulnerabilityCard'; import TopVulnerablePorts from './TopVulnerablePorts'; import TopVulnerableDomains from './TopVulnerableDomains'; @@ -26,6 +26,7 @@ import { REGION_FILTER_KEY } from 'components/RegionAndOrganizationFilters'; import { withSearch } from '@elastic/react-search-ui'; +import { FilterTags } from 'pages/Search/FilterTags'; export interface Point { id: string; @@ -53,7 +54,12 @@ let colorScale = scaleLinear() .domain([0, 1]) .range(['#c7e8ff', '#135787']); -const Risk: React.FC = ({ filters, addFilter }) => { +const Risk: React.FC = ({ + filters, + removeFilter, + searchTerm, + setSearchTerm +}) => { const { showMaps, user, apiPost } = useAuthContext(); const [stats, setStats] = useState(undefined); @@ -88,6 +94,20 @@ const Risk: React.FC = ({ filters, addFilter }) => { }; }, [filters]); + const filtersToDisplay = useMemo(() => { + if (searchTerm !== '') { + return [ + ...filters, + { + field: 'query', + values: [searchTerm], + onClear: () => setSearchTerm('', { shouldClearFilters: false }) + } + ]; + } + return filters; + }, [filters, searchTerm, setSearchTerm]); + const fetchStats = useCallback( async (orgId?: string) => { const { result } = await apiPost('/stats', { @@ -264,6 +284,12 @@ const Risk: React.FC = ({ filters, addFilter }) => {
+ + + {stats && ( From deba0abf386377676abecddef343f968bcb6bdd9 Mon Sep 17 00:00:00 2001 From: Thomas Johnson <123108455+hawkishpolicy@users.noreply.github.com> Date: Wed, 23 Oct 2024 09:26:29 -0400 Subject: [PATCH 08/14] Edited Back-to-Results in Domain/Vuln Details (#688) - Converted link in Vuln Details to a button with a back arrow icon. - Refined existing back button in Domain Details. - Removed "Vulnerability Details" from the top of the Vuln Details page. - Wrapped the Vuln Details page in a box to match the Domain Details page. - Ensured both buttons link back to their respective tables. --- frontend/src/components/DomainDetails.tsx | 19 ++++--------- .../src/pages/Vulnerability/Vulnerability.tsx | 28 +++++++------------ 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/frontend/src/components/DomainDetails.tsx b/frontend/src/components/DomainDetails.tsx index b21a036e..66b934ef 100644 --- a/frontend/src/components/DomainDetails.tsx +++ b/frontend/src/components/DomainDetails.tsx @@ -26,7 +26,6 @@ import { DefinitionList } from './DefinitionList'; import { differenceInCalendarDays, parseISO } from 'date-fns'; import { Webpage } from 'types'; import { useAuthContext } from 'context'; -import { Stack } from '@mui/system'; const PREFIX = 'DomainDetails'; @@ -336,21 +335,15 @@ export const DomainDetails: React.FC = (props) => { const webpageTree = generateWebpageTree(webpages); const webpageList = generateWebpageList(webpageTree); - const backToResults = () => { - history.push('/inventory'); - }; - return ( <> - history.goBack()} + startIcon={} > - - - + Back To Results + + {/* */}
diff --git a/frontend/src/pages/Vulnerability/Vulnerability.tsx b/frontend/src/pages/Vulnerability/Vulnerability.tsx index 9b127df7..6db5e3a3 100644 --- a/frontend/src/pages/Vulnerability/Vulnerability.tsx +++ b/frontend/src/pages/Vulnerability/Vulnerability.tsx @@ -1,10 +1,11 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Link, useParams, useHistory } from 'react-router-dom'; import { differenceInCalendarDays, parseISO } from 'date-fns'; -import { ChevronLeft, OpenInNew } from '@mui/icons-material'; +import { KeyboardBackspace, OpenInNew } from '@mui/icons-material'; import { AppBar, Box, + Button, Grid, IconButton, Link as LinkMui, @@ -446,25 +447,16 @@ export const Vulnerability: React.FC = () => { }; return ( - - - history.goBack()}> - - Go back - - + - - Vulnerability Details - + @@ -518,6 +510,6 @@ export const Vulnerability: React.FC = () => { - + ); }; From 2636fda021371a29af1c75a358d3907fe21ed91f Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Oct 2024 10:50:20 -0400 Subject: [PATCH 09/14] Edited express headers in api.js - Set content-type to application/json for files ending in .js - Set browser cache to 1 hour - Set content type to text/html for main index.html file --- frontend/scripts/api.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/scripts/api.js b/frontend/scripts/api.js index f6f2bca5..b945130d 100644 --- a/frontend/scripts/api.js +++ b/frontend/scripts/api.js @@ -76,9 +76,19 @@ app.use((req, res, next) => { next(); }); -app.use(express.static(path.join(__dirname, '../build'))); +app.use( + express.static(path.join(__dirname, '../build'), { + setHeaders: (res, path) => { + if (path.endsWith('.js')) { + res.setHeader('Content-Type', 'application/javascript'); + } + }, + maxAge: '1h' + }) +); app.use((req, res) => { + res.setHeader('Content-Type', 'text/html'); res.sendFile(path.join(__dirname, '../build/index.html')); }); From 8f8506666e1631a4ebd0837ae5e5038d1ae37dcc Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Oct 2024 16:04:44 -0400 Subject: [PATCH 10/14] Changed cache-control policy to no-cache - Browser should request latest version of static files from server --- frontend/scripts/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/scripts/api.js b/frontend/scripts/api.js index b945130d..8df26a53 100644 --- a/frontend/scripts/api.js +++ b/frontend/scripts/api.js @@ -83,7 +83,7 @@ app.use( res.setHeader('Content-Type', 'application/javascript'); } }, - maxAge: '1h' + maxAge: 'no-cache' }) ); From d67ba643cd886cb862901e7e12cfa5981cbb754c Mon Sep 17 00:00:00 2001 From: lwersiy <124970569+lwersiy@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:21:28 -0400 Subject: [PATCH 11/14] Refactored VulnerabilityBarChart to show severity level in the order of criticality (CRASM-783) (#690) * Refactored to include objects in mappedData array, and sorting from Null to Critical * removed hardcoded test values * removed console log * changed from Null to null to match the DMZ * Refactored Severity Levels Bar Graph x-axis - Added filtering and sorting logic to ensure the following: -- Incoming Severity Level data is formatted into Type Case. -- null values are placed in their own category. -- Values containing special characters are placed in the 'Other' category. -- The label 'Other' is recognized as a special case and is always placed at the end of the x-axis. - Added 'Other' category and the color blue to getSeverityColor util function. - Ensured those colors are used on the x-axis of the Severity Levels Bar Graph. * Removed console.log * Added logic for additional severity labels. * Cleaned up logic - Reduced the number of loops in the code. - Removed unnecessary code. - Edited color of "Other" bar in the bar chart. - null bar is now labeled "N/A" --------- Co-authored-by: Thomas --- .../src/pages/Risk/VulnerabilityBarChart.tsx | 61 +++++++++++++++---- frontend/src/pages/Risk/utils.ts | 3 +- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/frontend/src/pages/Risk/VulnerabilityBarChart.tsx b/frontend/src/pages/Risk/VulnerabilityBarChart.tsx index aa1214dd..53f3a1e2 100644 --- a/frontend/src/pages/Risk/VulnerabilityBarChart.tsx +++ b/frontend/src/pages/Risk/VulnerabilityBarChart.tsx @@ -73,18 +73,52 @@ const VulnerabilityBarChart = (props: { ); - // Map data to BarData structure - const mappedData: BarData[] = data.map((d) => ({ - id: d.id, - value: d.value - })); - - // Sort the data to ensure "Low", "Medium", and "High" appear in the desired order - const sortedData = [...mappedData].sort((a, b) => { - const order = ['Low', 'Medium', 'High']; - return order.indexOf(a.id) - order.indexOf(b.id); + // Place null values in "N/A" and capitalize the first letter of each word in the data. + const titleCaseData: BarData[] = data.map((d) => { + if (d.id === 'null') { + return { id: 'N/A', value: d.value }; + } else { + return { + id: d.id[0].toUpperCase() + d.id.slice(1).toLowerCase(), + value: d.value + }; + } }); + // Group the data by severity level and "Other". Sum the values for each group. + const groupedData = titleCaseData + .map((d) => { + const severityLevels = [ + 'N/A', + 'Low', + 'Medium', + 'High', + 'Critical', + 'Other' + ]; + if (severityLevels.includes(d.id)) { + return d; + } else { + return { id: 'Other', value: d.value }; + } + }) + .reduce((acc: { [key: string]: number }, curr) => { + if (acc[curr.id]) { + acc[curr.id] += curr.value; + } else { + acc[curr.id] = curr.value; + } + return acc; + }, {}); + + // Sort the data to ensure "N/A", "Low", "Medium", "High", and "Critical" appear in the desired order + const sortedData = Object.entries(groupedData) + .map(([id, value]) => ({ id, value })) + .sort((a, b) => { + const order = ['N/A', 'Low', 'Medium', 'High', 'Critical', 'Other']; + return order.indexOf(a.id) - order.indexOf(b.id); + }); + useEffect(() => { const label = `${title} bar chart. ` + @@ -105,7 +139,12 @@ const VulnerabilityBarChart = (props: { const severityColor = getSeverityColor({ id: indexValue }); const serviceColor = getServicesColor({ id: indexValue }); const color = - indexValue === 'Low' || indexValue === 'Medium' || indexValue === 'High' + indexValue === 'N/A' || + indexValue === 'Low' || + indexValue === 'Medium' || + indexValue === 'High' || + indexValue === 'Critical' || + indexValue === 'Other' ? severityColor : serviceColor; return color; diff --git a/frontend/src/pages/Risk/utils.ts b/frontend/src/pages/Risk/utils.ts index 3855d2f3..524c1b89 100644 --- a/frontend/src/pages/Risk/utils.ts +++ b/frontend/src/pages/Risk/utils.ts @@ -13,10 +13,11 @@ export const getAllVulnColor = () => { return '#ce80ed'; }; export const getSeverityColor = ({ id }: { id: string }) => { - if (id === 'null' || id === '') return '#EFF1F5'; + if (id === 'N/A' || id === '') return '#EFF1F5'; else if (id === 'Low') return '#ffe100'; else if (id === 'Medium') return '#f66e1f'; else if (id === 'High') return '#ed240e'; + else if (id === 'Other') return '#6BA7F5'; else return '#540C03'; }; export const getCVSSColor = (score: number) => { From 98306bc88cbcde28d8881c10c73873f31455e623 Mon Sep 17 00:00:00 2001 From: Thomas Johnson <123108455+hawkishpolicy@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:39:40 -0400 Subject: [PATCH 12/14] Edited cache control (#700) - HTTP cache control header is set to no-cache, no-store, must-revalidate. Browser cache is set to no-cache, no-store, must-revalidate. --- frontend/scripts/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/scripts/api.js b/frontend/scripts/api.js index 8df26a53..3f1332dd 100644 --- a/frontend/scripts/api.js +++ b/frontend/scripts/api.js @@ -67,7 +67,7 @@ app.use( //Middleware to set Cache-Control headers app.use((req, res, next) => { - res.setHeader('Cache-Control', 'private, max-age=3600'); + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); next(); }); @@ -83,7 +83,7 @@ app.use( res.setHeader('Content-Type', 'application/javascript'); } }, - maxAge: 'no-cache' + maxAge: 'no-cache, no-store, must-revalidate' }) ); From 28cf4cb5b56001c61fe780fa1ee11cc67ad01113 Mon Sep 17 00:00:00 2001 From: Thomas Johnson <123108455+hawkishpolicy@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:40:33 -0400 Subject: [PATCH 13/14] Edite Cache-Control headrs in app.ts (#701) - Set to no-cache, no-store, must-revalidate --- backend/src/api/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/app.ts b/backend/src/api/app.ts index e9ea583a..030991f8 100644 --- a/backend/src/api/app.ts +++ b/backend/src/api/app.ts @@ -143,7 +143,7 @@ app.use( //Middleware to set Cache-Control headers app.use((req, res, next) => { - res.setHeader('Cache-Control', 'private, must-revalidate, max-age=3600'); + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); next(); }); From 1805b308120ad1ae90666c1a3c569d40b3db8e2e Mon Sep 17 00:00:00 2001 From: Thomas Johnson <123108455+hawkishpolicy@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:03:00 -0400 Subject: [PATCH 14/14] Edited RegionUsers.tsx (#702) - Made lastLoggedIn more readable. - Added boolean to display lastLoggedIn or "None" if it is null. --- frontend/src/pages/RegionUsers/RegionUsers.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/RegionUsers/RegionUsers.tsx b/frontend/src/pages/RegionUsers/RegionUsers.tsx index 30fbfbae..8771d0fd 100644 --- a/frontend/src/pages/RegionUsers/RegionUsers.tsx +++ b/frontend/src/pages/RegionUsers/RegionUsers.tsx @@ -16,6 +16,7 @@ import DoneIcon from '@mui/icons-material/Done'; import { CheckCircleOutline as CheckIcon } from '@mui/icons-material'; import CloseIcon from '@mui/icons-material/Close'; import { useUserLevel } from 'hooks/useUserLevel'; +import { formatDate, parseISO } from 'date-fns'; type DialogStates = { isOrgDialogOpen: boolean; @@ -37,7 +38,10 @@ const transformData = (data: User[]): User[] => { return data.map(({ roles, ...user }) => ({ ...user, roles, - organizations: roles.map((role) => ' ' + role.organization.name) + organizations: roles.map((role) => ' ' + role.organization.name), + lastLoggedIn: user.lastLoggedIn + ? formatDate(parseISO(user.lastLoggedIn), 'MM/dd/yyyy hh:mm a') + : 'None' })); }; export const RegionUsers: React.FC = () => {