From 1a0e227e55e5ccb97d171f890d9054ec836d5b26 Mon Sep 17 00:00:00 2001 From: Lucas Bruno Date: Sat, 22 Jan 2022 07:24:30 -0300 Subject: [PATCH] feature:manager - New screen to handler vulnerabilities (#542) New screen of horusec-manager to handler vulnerabilities with group of files. ![](https://user-images.githubusercontent.com/69604366/150204407-c5ace0a6-14ba-4265-a15e-fff0a8730c82.gif) Signed-off-by: lucas.bruno --- .../without_application_admin_spec.ts | 38 +- .../without_application_admin_spec.ts | 38 +- manager/src/assets/svg/CRITICAL.svg | 5 + manager/src/assets/svg/HIGH.svg | 5 + manager/src/assets/svg/INFO.svg | 5 + manager/src/assets/svg/LOW.svg | 5 + manager/src/assets/svg/MEDIUM.svg | 8 + manager/src/assets/svg/UNKNOWN.svg | 5 + manager/src/assets/svg/info.svg | 18 - manager/src/components/Button/index.tsx | 3 + manager/src/components/Button/styled.ts | 16 + manager/src/components/Pagination/index.tsx | 30 +- manager/src/components/Pagination/styled.ts | 107 --- manager/src/components/SearchBar/index.tsx | 13 +- manager/src/components/Select/index.tsx | 7 +- manager/src/config/i18n/enUS.json | 6 +- manager/src/config/i18n/esES.json | 6 +- manager/src/config/i18n/ptBR.json | 6 +- manager/src/config/themes/dark.ts | 1 + manager/src/helpers/formatters/date.ts | 16 +- manager/src/helpers/hooks/useLanguage.ts | 19 + manager/src/helpers/hooks/usePermissions.ts | 1 + manager/src/helpers/interfaces/FIlterVuln.ts | 1 + .../src/helpers/interfaces/Vulnerability.ts | 5 + .../src/helpers/interfaces/VulnerableFile.ts | 25 + manager/src/pages/Overview/Tokens/index.tsx | 1 - manager/src/pages/Overview/Tokens/styled.ts | 2 +- .../Vulnerabilities/Details/index.tsx | 126 --- .../Vulnerabilities/Details/styled.ts | 107 --- .../pages/Overview/Vulnerabilities/index.tsx | 774 +++++++++++------- .../pages/Overview/Vulnerabilities/styled.ts | 380 ++++++++- manager/src/services/vulnerabilities.ts | 68 +- 32 files changed, 1050 insertions(+), 797 deletions(-) create mode 100644 manager/src/assets/svg/CRITICAL.svg create mode 100644 manager/src/assets/svg/HIGH.svg create mode 100644 manager/src/assets/svg/INFO.svg create mode 100644 manager/src/assets/svg/LOW.svg create mode 100644 manager/src/assets/svg/MEDIUM.svg create mode 100644 manager/src/assets/svg/UNKNOWN.svg delete mode 100644 manager/src/assets/svg/info.svg delete mode 100644 manager/src/components/Pagination/styled.ts create mode 100644 manager/src/helpers/interfaces/VulnerableFile.ts delete mode 100644 manager/src/pages/Overview/Vulnerabilities/Details/index.tsx delete mode 100644 manager/src/pages/Overview/Vulnerabilities/Details/styled.ts diff --git a/e2e/cypress/src/integration/auth-horusec/without_application_admin_spec.ts b/e2e/cypress/src/integration/auth-horusec/without_application_admin_spec.ts index 4e59c25e6..f96cad70a 100644 --- a/e2e/cypress/src/integration/auth-horusec/without_application_admin_spec.ts +++ b/e2e/cypress/src/integration/auth-horusec/without_application_admin_spec.ts @@ -331,37 +331,23 @@ function CheckIfExistsVulnerabilitiesAndCanUpdateSeverityAndStatus(): void { cy.get("li").contains("Vulnerabilities").click(); cy.wait(1500); - cy.get('.MuiTableBody-root > :nth-child(1) > :nth-child(3)').invoke('text') - .then((firstHash)=>{ - console.log(firstHash) - expect(firstHash).not.to.equal(""); - expect(firstHash).not.to.equal(undefined); - expect(firstHash).not.to.equal(null); + // Select first vulnerability + cy.get('.file-list > :nth-child(1)').click(); - // Select first vulnerability and open Severity dropdown - cy.get(":nth-child(1) > .center > .MuiFormControl-root > .MuiInputBase-root > #select").click(); - cy.wait(500); + // Open severity dropdown of first vulnerability + cy.get(".severity-dropdown").first().click(); - // Change severity to HIGH - cy.get('[data-value="HIGH"]').click(); + // Change severity to CRITICAL + cy.get('[data-value="CRITICAL"]').click(); - // Select first vulnerability and open status dropdown - cy.get(":nth-child(1) > :nth-child(2) > .MuiFormControl-root > .MuiInputBase-root > #select").click(); + // Open status dropdown of first vulnerability + cy.get(".status-dropdown").first().click(); - // Change status to Risk Accepted - cy.get('[data-value="Risk Accepted"]').click(); - cy.get("button").contains("Update Vulnerabilities").click(); - cy.wait(1500); + // Change status to Risk Accepted + cy.get('[data-value="Risk Accepted"]').click(); - // Open modal of vulnerability and check if details exists - cy.get("[data-testid=\"icon-info\"]").first().click(); - cy.contains("Vulnerability Details").should("exist") - cy.wait(1500); - - cy.get("[data-testid=\"icon-close\"]").first().click(); - - cy.contains(firstHash).should("not.exist"); - }) + cy.get("button.save-vulnerabilities").click(); + cy.wait(1500); } function CreateUserAndInviteToExistingWorkspace(): void { diff --git a/e2e/cypress/src/integration/auth-keycloak/without_application_admin_spec.ts b/e2e/cypress/src/integration/auth-keycloak/without_application_admin_spec.ts index a4a2fffa4..21f78ac7d 100644 --- a/e2e/cypress/src/integration/auth-keycloak/without_application_admin_spec.ts +++ b/e2e/cypress/src/integration/auth-keycloak/without_application_admin_spec.ts @@ -333,37 +333,23 @@ function CheckIfExistsVulnerabilitiesAndCanUpdateSeverityAndStatus(): void { cy.get("li").contains("Vulnerabilities").click(); cy.wait(1500); - cy.get('.MuiTableBody-root > :nth-child(1) > :nth-child(3)').invoke('text') - .then((firstHash)=>{ - console.log(firstHash) - expect(firstHash).not.to.equal(""); - expect(firstHash).not.to.equal(undefined); - expect(firstHash).not.to.equal(null); + // Select first vulnerability + cy.get('.file-list > :nth-child(1)').click(); - // Select first vulnerability and open Severity dropdown - cy.get(":nth-child(1) > .center > .MuiFormControl-root > .MuiInputBase-root > #select").click(); - cy.wait(500); + // Open severity dropdown of first vulnerability + cy.get(".severity-dropdown").first().click(); - // Change severity to HIGH - cy.get('[data-value="HIGH"]').click(); + // Change severity to CRITICAL + cy.get('[data-value="CRITICAL"]').click(); - // Select first vulnerability and open status dropdown - cy.get(":nth-child(1) > :nth-child(2) > .MuiFormControl-root > .MuiInputBase-root > #select").click(); + // Open status dropdown of first vulnerability + cy.get(".status-dropdown").first().click(); - // Change status to Risk Accepted - cy.get('[data-value="Risk Accepted"]').click(); - cy.get("button").contains("Update Vulnerabilities").click(); - cy.wait(1500); + // Change status to Risk Accepted + cy.get('[data-value="Risk Accepted"]').click(); - // Open modal of vulnerability and check if details exists - cy.get("[data-testid=\"icon-info\"]").first().click(); - cy.contains("Vulnerability Details").should("exist") - cy.wait(1500); - - cy.get("[data-testid=\"icon-close\"]").first().click(); - - cy.contains(firstHash).should("not.exist"); - }) + cy.get("button.save-vulnerabilities").click(); + cy.wait(1500); } function CreateUserAndInviteToExistingWorkspace(): void { diff --git a/manager/src/assets/svg/CRITICAL.svg b/manager/src/assets/svg/CRITICAL.svg new file mode 100644 index 000000000..a3f735aae --- /dev/null +++ b/manager/src/assets/svg/CRITICAL.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/manager/src/assets/svg/HIGH.svg b/manager/src/assets/svg/HIGH.svg new file mode 100644 index 000000000..e548f06cb --- /dev/null +++ b/manager/src/assets/svg/HIGH.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/manager/src/assets/svg/INFO.svg b/manager/src/assets/svg/INFO.svg new file mode 100644 index 000000000..49e497209 --- /dev/null +++ b/manager/src/assets/svg/INFO.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/manager/src/assets/svg/LOW.svg b/manager/src/assets/svg/LOW.svg new file mode 100644 index 000000000..86e817db8 --- /dev/null +++ b/manager/src/assets/svg/LOW.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/manager/src/assets/svg/MEDIUM.svg b/manager/src/assets/svg/MEDIUM.svg new file mode 100644 index 000000000..372a27640 --- /dev/null +++ b/manager/src/assets/svg/MEDIUM.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/manager/src/assets/svg/UNKNOWN.svg b/manager/src/assets/svg/UNKNOWN.svg new file mode 100644 index 000000000..b9e5c9130 --- /dev/null +++ b/manager/src/assets/svg/UNKNOWN.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/manager/src/assets/svg/info.svg b/manager/src/assets/svg/info.svg deleted file mode 100644 index 7554b5dc7..000000000 --- a/manager/src/assets/svg/info.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/manager/src/components/Button/index.tsx b/manager/src/components/Button/index.tsx index a14b60865..3145492af 100644 --- a/manager/src/components/Button/index.tsx +++ b/manager/src/components/Button/index.tsx @@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next'; interface ButtonProps extends ButtonHTMLAttributes { text: string; outline?: boolean; + outlinePrimary?: boolean; rounded?: boolean; opaque?: boolean; ghost?: boolean; @@ -39,6 +40,7 @@ interface ButtonProps extends ButtonHTMLAttributes { const Button: React.FC = ({ text, outline, + outlinePrimary, rounded, opaque, ghost, @@ -64,6 +66,7 @@ const Button: React.FC = ({ {...props} isLoading={isLoading} outline={outline} + outlinePrimary={outlinePrimary} rounded={rounded} opaque={opaque} ghost={ghost} diff --git a/manager/src/components/Button/styled.ts b/manager/src/components/Button/styled.ts index 3bf61c100..d645ff588 100644 --- a/manager/src/components/Button/styled.ts +++ b/manager/src/components/Button/styled.ts @@ -18,6 +18,7 @@ import styled, { css } from 'styled-components'; interface ButtonProps { outline?: boolean; + outlinePrimary?: boolean; rounded?: boolean; opaque?: boolean; ghost?: boolean; @@ -79,6 +80,21 @@ const Button = styled.button` border: 1px solid ${({ theme }) => theme.colors.button.border}; `}; + ${({ outlinePrimary, isLoading }) => + outlinePrimary && isLoading + ? css` + background: ${({ theme }) => theme.colors.button.outlineBackground}; + border: 1px solid ${({ theme }) => theme.colors.button.disableInDark} !important; + ` + : null} + + ${({ outlinePrimary }) => + outlinePrimary && + css` + background: ${({ theme }) => theme.colors.button.outlineBackground}; + border: 1px solid ${({ theme }) => theme.colors.button.primary}; + `}; + ${({ ghost }) => ghost && css` diff --git a/manager/src/components/Pagination/index.tsx b/manager/src/components/Pagination/index.tsx index 29022f5c0..3ee9d2eab 100644 --- a/manager/src/components/Pagination/index.tsx +++ b/manager/src/components/Pagination/index.tsx @@ -42,20 +42,22 @@ const Pagination: React.FC = ({ onChange, pagination }) => { }; return ( - - `${from}-${to} ${t('GENERAL.PAGINATION.OF')} ${ - count !== -1 ? count : to - }` - } - /> + pagination.totalItems > 0 && ( + + `${from}-${to} ${t('GENERAL.PAGINATION.OF')} ${ + count !== -1 ? count : to + }` + } + /> + ) ); }; diff --git a/manager/src/components/Pagination/styled.ts b/manager/src/components/Pagination/styled.ts deleted file mode 100644 index bb396837c..000000000 --- a/manager/src/components/Pagination/styled.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright 2021 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import styled, { css } from 'styled-components'; -import { Icon } from 'components'; - -interface ListItemProps { - isVisible: boolean; -} - -const Wrapper = styled.div` - display: flex; - flex-direction: row; - justify-content: flex-end; - align-items: center; - padding: 10px; - height: 80px; -`; - -const ArrowIcon = styled(Icon)` - :hover { - cursor: pointer; - transform: scale(1.3); - } -`; - -const Next = styled(ArrowIcon)``; - -const Previous = styled(ArrowIcon)` - margin-right: 30px; -`; - -const Text = styled.span` - color: ${({ theme }) => theme.colors.text.primary}; - font-size: ${({ theme }) => theme.metrics.fontSize.medium}; - margin: 0px 2px; -`; - -const PagesWrapper = styled.div` - display: block; - margin-right: 30px; -`; - -const ItemWrapper = styled.div` - cursor: pointer; - display: flex; - align-items: center; - margin-right: 30px; - position: relative; -`; - -const ListItems = styled.ul` - background-color: ${({ theme }) => theme.colors.background.highlight}; - width: 80px; - position: absolute; - top: 20px; - right: 0; - z-index: 3; - height: 0; - opacity: 0; - transition: all 0.5s; - visibility: hidden; - - ${({ isVisible }) => - isVisible && - css` - height: 80px; - opacity: 1; - visibility: visible; - `}; -`; - -const Item = styled.li` - list-style: none; - color: ${({ theme }) => theme.colors.text.primary}; - font-size: ${({ theme }) => theme.metrics.fontSize.medium}; - padding: 5px; - cursor: pointer; - - :hover { - background-color: ${({ theme }) => theme.colors.background.overlap}; - } -`; - -export default { - Wrapper, - Previous, - Next, - Text, - PagesWrapper, - ItemWrapper, - ListItems, - Item, -}; diff --git a/manager/src/components/SearchBar/index.tsx b/manager/src/components/SearchBar/index.tsx index 8ee6b5fd2..b0680dec9 100644 --- a/manager/src/components/SearchBar/index.tsx +++ b/manager/src/components/SearchBar/index.tsx @@ -23,15 +23,24 @@ interface Props extends InputHTMLAttributes { placeholder: string; } -const SearchBar: React.FC = ({ placeholder, onSearch, ...rest }) => { +const SearchBar: React.FC = ({ + placeholder, + onSearch, + onBlur, + onFocus, + style, + ...rest +}) => { return ( - + onSearch(event.target.value)} + onBlur={onBlur} + onFocus={onFocus} /> ); diff --git a/manager/src/components/Select/index.tsx b/manager/src/components/Select/index.tsx index a5d18f22a..0d7f40b12 100644 --- a/manager/src/components/Select/index.tsx +++ b/manager/src/components/Select/index.tsx @@ -44,6 +44,7 @@ interface Props { testId?: string; placeholder?: string; style?: CSSProperties; + wrapperStyle?: CSSProperties; variant?: SelectProps['variant']; } @@ -58,11 +59,15 @@ const SelectInput: React.FC = ({ placeholder, style, variant, + wrapperStyle, }) => { const { t } = useTranslation(); return ( - + {label && {label}} { - setFilters((state) => ({ ...state, vulnSeverity: item })); - setRefresh({ - filter: { ...filters, vulnSeverity: item }, - page: { ...pagination, currentPage: INITIAL_PAGE }, - }); - }} + onChangeValue={(item) => + setFilters((state) => ({ ...state, vulnSeverity: item })) + } /> - { - setFilters((state) => ({ ...state, vulnType: item })); - setRefresh({ - filter: { ...filters, vulnType: item }, - page: { ...pagination, currentPage: INITIAL_PAGE }, + onChangeValue={(item) => + setFilters((state) => ({ ...state, vulnType: item })) + } + /> + + + + + + + ); + + const renderMenuUpdating = () => { + if (vulnerabilitiesToUpdate.length > 0) { + return ( + + + {vulnerabilitiesToUpdate.length} + {t('VULNERABILITIES_SCREEN.UPDATE_VULNERABILITY')} + + + +