From 6e9618224ad017373f8b05b0ae5a5dd23ab4d487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 18 Apr 2024 23:35:33 +0200 Subject: [PATCH 1/2] Support single tenant Service health report --- src/views/tenant/administration/ServiceHealth.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/tenant/administration/ServiceHealth.jsx b/src/views/tenant/administration/ServiceHealth.jsx index 30d3a5e0199c..7da9f0bc195c 100644 --- a/src/views/tenant/administration/ServiceHealth.jsx +++ b/src/views/tenant/administration/ServiceHealth.jsx @@ -1,4 +1,5 @@ import React from 'react' +import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { CellTip } from 'src/components/tables' @@ -37,6 +38,7 @@ const columns = [ ] const ServiceHealth = () => { + const currentTenant = useSelector((state) => state.app.currentTenant) return ( { datatable={{ columns, path: '/api/ListServiceHealth', + params: { + tenantFilter: currentTenant.customerId, + displayName: currentTenant.displayName, + defaultDomainName: currentTenant.defaultDomainName, + }, reportName: `Service-Health-Report`, }} /> From 55601100ad2ed0a2d514054b076f10d586e32986 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 21 Apr 2024 16:38:21 +0200 Subject: [PATCH 2/2] added securescore overviews. --- src/data/standards.json | 41 +- src/importsMap.jsx | 1 + src/routes.json | 6 + src/scss/_themes.scss | 2 +- .../tenant/administration/SecureScore.jsx | 416 ++++++++++++++++++ 5 files changed, 452 insertions(+), 14 deletions(-) create mode 100644 src/views/tenant/administration/SecureScore.jsx diff --git a/src/data/standards.json b/src/data/standards.json index cab53f3144d6..ecde5a79d459 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -38,7 +38,7 @@ { "name": "standards.AuditLog", "cat": "Global Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "mip_search_auditlog"], "helpText": "Enables the Unified Audit Log for tracking and auditing activities. Also runs Enable-OrganizationCustomization if necessary.", "addedComponent": [], "label": "Enable the Unified Audit Log", @@ -63,7 +63,7 @@ { "name": "standards.EnableCustomerLockbox", "cat": "Global Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "CustomerLockBoxEnabled"], "helpText": "Enables Customer Lockbox that offers an approval process for Microsoft support to access organization data", "addedComponent": [], "label": "Enable Customer Lockbox", @@ -103,7 +103,7 @@ { "name": "standards.ActivityBasedTimeout", "cat": "Global Standards", - "tag": ["mediumimpact", "CIS"], + "tag": ["mediumimpact", "CIS", "spo_idle_session_timeout"], "helpText": "Enables and sets Idle session timeout for Microsoft 365 to 1 hour. This policy affects most M365 web apps", "addedComponent": [], "label": "Enable 1 hour Activity based Timeout", @@ -225,7 +225,7 @@ { "name": "standards.PasswordExpireDisabled", "cat": "Entra (AAD) Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "PWAgePolicyNew"], "helpText": "Disables the expiration of passwords for the tenant by setting the password expiration policy to never expire for any user.", "addedComponent": [], "label": "Do not expire passwords", @@ -535,7 +535,7 @@ { "name": "standards.EnableMailTips", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_mailtipsenabled"], "helpText": "Enables all MailTips in Outlook. MailTips are the notifications Outlook and Outlook on the web shows when an email you create, meets some requirements", "addedComponent": [ { @@ -582,7 +582,7 @@ { "name": "standards.EnableMailboxAuditing", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_mailboxaudit"], "helpText": "Enables Mailbox auditing for all mailboxes and on tenant level. Disables audit bypass on all mailboxes. Unified Audit Log needs to be enabled for this standard to function.", "addedComponent": [], "label": "Enable Mailbox auditing", @@ -664,7 +664,7 @@ { "name": "standards.DisableExternalCalendarSharing", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_individualsharing"], "helpText": "Disables the ability for users to share their calendar with external users. Only for the default policy, so exclusions can be made if needed.", "addedComponent": [], "label": "Disable external calendar sharing", @@ -674,7 +674,7 @@ { "name": "standards.DisableAdditionalStorageProviders", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_storageproviderrestricted"], "helpText": "Disables the ability for users to open files in Outlook on the Web, from other providers such as Box, Dropbox, Facebook, Google Drive, OneDrive Personal, etc.", "addedComponent": [], "label": "Disable additional storage providers in OWA", @@ -684,7 +684,7 @@ { "name": "standards.DisableOutlookAddins", "cat": "Exchange Standards", - "tag": ["mediumimpact", "CIS"], + "tag": ["mediumimpact", "CIS", "exo_outlookaddins"], "helpText": "Disables the ability for users to install add-ins in Outlook. This is to prevent users from installing malicious add-ins.", "addedComponent": [], "label": "Disable users from installing add-ins in Outlook", @@ -765,7 +765,7 @@ { "name": "standards.SafeLinksPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "mdo_safelinksforemail", "mdo_safelinksforOfficeApps"], "helpText": "This creates a safelink policy that automatically scans, tracks, and and enables safe links for Email, Office, and Teams for both external and internal senders", "addedComponent": [ { @@ -791,7 +791,16 @@ { "name": "standards.AntiPhishPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": [ + "lowimpact", + "CIS", + "mdo_safeattachments", + "mdo_highconfidencespamaction", + "mdo_highconfidencephishaction", + "mdo_phisspamacation", + "mdo_spam_notifications_only_for_admins", + "mdo_antiphishingpolicies" + ], "helpText": "This creates a Anti-Phishing policy that automatically enables Mailbox Intelligence and spoofing, optional switches for Mailtips.", "addedComponent": [ { @@ -870,7 +879,13 @@ { "name": "standards.SafeAttachmentPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": [ + "lowimpact", + "CIS", + "mdo_safedocuments", + "mdo_commonattachmentsfilter", + "mdo_safeattachmentpolicy" + ], "helpText": "This creates a Safe Attachment policy", "addedComponent": [ { @@ -946,7 +961,7 @@ { "name": "standards.MalwareFilterPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "mdo_zapspam", "mdo_zapphish", "mdo_zapmalware"], "helpText": "This creates a Malware filter policy that enables the default File filter and Zero-hour auto purge for malware.", "addedComponent": [ { diff --git a/src/importsMap.jsx b/src/importsMap.jsx index ab783b321471..0121566555bd 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -125,6 +125,7 @@ import React from 'react' "/license": React.lazy(() => import('./views/pages/license/License')), "/cipp/settings": React.lazy(() => import('./views/cipp/app-settings/CIPPSettings')), "/cipp/setup": React.lazy(() => import('./views/cipp/Setup')), + "/tenant/administration/securescore": React.lazy(() => import('./views/tenant/administration/SecureScore')), "/tenant/administration/gdap": React.lazy(() => import('./views/tenant/administration/GDAPWizard')), "/tenant/administration/gdap-invite": React.lazy(() => import('./views/tenant/administration/GDAPInviteWizard')), "/tenant/administration/gdap-role-wizard": React.lazy(() => import('./views/tenant/administration/GDAPRoleWizard')), diff --git a/src/routes.json b/src/routes.json index db9ee316185f..3d8eb476d3fb 100644 --- a/src/routes.json +++ b/src/routes.json @@ -864,6 +864,12 @@ "component": "views/cipp/Setup", "allowedRoles": ["admin"] }, + { + "path": "/tenant/administration/securescore", + "name": "Secure Score Management", + "component": "views/tenant/administration/SecureScore", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/tenant/administration/gdap", "name": "GDAP Wizard", diff --git a/src/scss/_themes.scss b/src/scss/_themes.scss index 3c1be2d0cecf..f05c2be45f9b 100644 --- a/src/scss/_themes.scss +++ b/src/scss/_themes.scss @@ -479,7 +479,7 @@ --cui-toast-color: var(--cui-color-black); --cui-toast-header-color: var(--cui-color-black); --cui-card-cap-color: var(--cyberdrain-white); - --cui-card-cap-bg: var(--cui-color-dark); + --cui-card-cap-bg: var(--cui-color-gray-hover); --cui-tertiary-bg: var(--cui-bgcolor-table-header); // CIPP Impact theme variables. --cipp-toast-bg: var(--cui-color-header-bar); diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx new file mode 100644 index 000000000000..423d6c8611de --- /dev/null +++ b/src/views/tenant/administration/SecureScore.jsx @@ -0,0 +1,416 @@ +import React, { useEffect, useRef } from 'react' +import { + CBadge, + CButton, + CCard, + CCardBody, + CCardFooter, + CCardHeader, + CCardText, + CCardTitle, + CCol, + CFormInput, + CFormSelect, + CFormSwitch, + CRow, +} from '@coreui/react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCheck, faTimes, faExclamation } from '@fortawesome/free-solid-svg-icons' +import { CippTable } from 'src/components/tables' +import { CippPage } from 'src/components/layout/CippPage' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { useSelector } from 'react-redux' +import Skeleton from 'react-loading-skeleton' +import standards from 'src/data/standards' +import { useNavigate } from 'react-router-dom' +import { ModalService } from 'src/components/utilities' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' + +const SecureScore = () => { + const textRef = useRef() + const selectRef = useRef() + const currentTenant = useSelector((state) => state.app.currentTenant) + const [viewMode, setViewMode] = React.useState(false) + const [translateData, setTranslatedData] = React.useState([]) + const [translateState, setTranslateSuccess] = React.useState(false) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [sortBy, setSortBy] = React.useState() + const { + data: securescore = [], + isFetching, + isSuccess, + } = useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + tenantFilter: currentTenant.defaultDomainName, + Endpoint: 'security/secureScores', + $top: 1, + NoPagination: true, + }, + }) + + const { data: securescoreTranslation = [], isSuccess: isSuccessTranslation } = + useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + tenantFilter: currentTenant.defaultDomainName, + Endpoint: 'security/secureScoreControlProfiles', + $top: 999, + NoPagination: true, + }, + }) + + useEffect(() => { + if (isSuccess) { + setTranslatedData(securescore.Results[0]) + setTranslateSuccess(true) + } + }, [isSuccess, securescore.Results]) + + useEffect(() => { + if (isSuccess && isSuccessTranslation) { + const updatedControlScores = translateData.controlScores.map((control) => { + const translation = securescoreTranslation.Results?.find( + (controlTranslation) => controlTranslation.id === control.controlName, + ) + const remediation = standards.find((standard) => standard.tag.includes(control.controlName)) + return { + ...control, + title: translation?.title, + threats: translation?.threats, + complianceInformation: translation?.complianceInformation, + actionUrl: remediation + ? '/tenant/standards/list-applied-standards' + : translation?.actionUrl, + remediation: remediation + ? `1. Enable the CIPP Standard: ${remediation.label}` + : translation?.remediation, + remediationImpact: translation?.remediationImpact, + implementationCost: translation?.implementationCost, + tier: translation?.tier, + userImpact: translation?.userImpact, + vendorInformation: translation?.vendorInformation, + controlStateUpdates: translation?.controlStateUpdates[0] + ? translation.controlStateUpdates + : [], + } + }) + + updatedControlScores.sort((a, b) => { + return b['scoreInPercentage'] - a['scoreInPercentage'] + }) + setTranslatedData((prevData) => ({ + ...prevData, + controlScores: updatedControlScores, + })) + } + }, [isSuccess, isSuccessTranslation, securescoreTranslation.Results]) + //create open function, if remediation starts with https, open in new tab. Else, use router to navigate to the remediation page. + const navigate = useNavigate() + + const openRemediation = (url) => { + if (url.startsWith('https')) { + window.open(url, '_blank') + } else { + navigate(url) + } + } + const openResolution = (control) => { + ModalService.confirm({ + key: control, + body: ( +
+
+ +
+
+ +
+
+ ), + title: 'Confirm', + onConfirm: () => + genericPostRequest({ + path: '/api/ExecUpdateSecureScore', + values: { + controlName: control.controlName, + resolutionType: selectRef.current.value, + reason: textRef.current.value, + tenantFilter: currentTenant.defaultDomainName, + vendorinformation: control.vendorInformation, + }, + }), + }) + } + + const columns = [ + { + name: 'Task Title', + selector: (row) => row['title'], + sortable: true, + cell: (row) => CellTip(row['title']), + exportSelector: 'title', + }, + { + name: 'Percentage Complete', + selector: (row) => row['scoreInPercentage'], + sortable: true, + cell: (row) => CellTip(row['scoreInPercentage']), + exportSelector: 'scoreInPercentage', + }, + { + name: 'Remediation', + selector: (row) => row['actionUrl'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'actionUrl', + }, + ] + + return ( + <> + + + + + Overview mode + + + + setViewMode(!viewMode)} /> + + + + + + + + Current Score + + + + {isFetching && } + {translateState && ( + <> +

+ {Math.round( + (translateData?.currentScore / translateData?.maxScore) * 100 * 10, + ) / 10} + % +

+ + {translateData?.currentScore} of {translateData?.maxScore} points + + + )} +
+
+
+
+ + + + Compared Score (Similiar sized business) + + + + {isFetching && } + {translateState && ( + <> +

+ { + //calculate percentage, round to 1 dec. + Math.round( + (translateData?.averageComparativeScores[1]?.averageScore / + translateData?.maxScore) * + 100 * + 10, + ) / 10 + } + % +

+ + {translateData?.averageComparativeScores[1]?.averageScore} of{' '} + {translateData?.maxScore} points + + + )} +
+
+
+
+ + + + Compared Score (All businesses) + + + + {isFetching && } + {translateState && ( + <> +

+ { + //calculate percentage, round to 1 dec. + Math.round( + (translateData?.averageComparativeScores[0]?.averageScore / + translateData?.maxScore) * + 100 * + 10, + ) / 10 + } + % +

+ + {translateData?.averageComparativeScores[0]?.averageScore} of{' '} + {translateData?.maxScore} points + + + )} +
+
+
+
+
+ + {viewMode && translateData.controlScores.length > 1 && ( + + + Best Practice Report + + + + + + )} + {translateState && !viewMode && ( + <> + + {translateData?.controlScores?.map((info, idx) => ( + + + + {info.title} + {console.log(info)} + + + + + + {info.scoreInPercentage === 100 + ? `100% ${ + info.controlStateUpdates?.length > 0 && + info.controlStateUpdates[0].state !== 'Default' + ? `(${info?.controlStateUpdates[0]?.state})` + : '' + }` + : `${info.scoreInPercentage}% ${ + info.controlStateUpdates?.length > 0 && + info.controlStateUpdates[0].state !== 'Default' + ? `(${info?.controlStateUpdates[0]?.state})` + : '' + } + `} + + + + +
Description
+ +
+ + + {info.scoreInPercentage !== 100 && ( + +
Remediation Recommendation
+ + { +
+ } + + + )} + + {info.threats?.length > 0 && ( + <> +
Threats
+ {info.threats?.map((threat, idx) => ( + + {threat} + + ))} + + )} +
+ + {info.complianceInformation > 0 && ( + <> +
Compliance Frameworks
+ {info.complianceInformation?.map((framework, idx) => ( + + {framework.certificationName} -{' '} + {framework.certificationControls[0]?.name} + + ))} + + )} +
+ + + openRemediation(info.actionUrl)} + className="me-3" + > + Remediate + + openResolution(info)} className="me-3"> + Change Status + + + + + ))} + + + )} + + + ) +} + +export default SecureScore