diff --git a/src/api.js b/src/api.js index 0596edf9..916e066b 100644 --- a/src/api.js +++ b/src/api.js @@ -90,13 +90,16 @@ const endpoints = { `/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Manifests {Digest Platform {Os Arch} Size} Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor IsDeletable } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors IsStarred IsBookmarked NewestImage {RepoName IsSigned SignatureInfo { Tool IsTrusted Author } Vulnerabilities {MaxSeverity Count} Manifests {Digest} Tag Vendor Title Documentation DownloadCount Source Description Licenses}}}}`, detailedImageInfo: (name, tag) => `/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName IsSigned SignatureInfo { Tool IsTrusted Author } Vulnerabilities {MaxSeverity Count} Referrers {MediaType ArtifactType Size Digest Annotations{Key Value}} Tag Manifests {History {Layer {Size Digest} HistoryDescription {CreatedBy EmptyLayer}} Digest ConfigDigest LastUpdated Size Platform {Os Arch}} Vendor Licenses }}`, - vulnerabilitiesForRepo: (name, { pageNumber = 1, pageSize = 15 }, searchTerm = '') => { + vulnerabilitiesForRepo: (name, { pageNumber = 1, pageSize = 15 }, searchTerm = '', excludedTerm = '') => { let query = `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}", requestedPage: {limit:${pageSize} offset:${ (pageNumber - 1) * pageSize }}`; if (!isEmpty(searchTerm)) { query += `, searchedCVE: "${searchTerm}"`; } + if (!isEmpty(excludedTerm)) { + query += `, excludedCVE: "${excludedTerm}"`; + } return `${query}){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`; }, allVulnerabilitiesForRepo: (name) => diff --git a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx index f80612e2..da711481 100644 --- a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx +++ b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx @@ -30,6 +30,9 @@ import exportFromJSON from 'export-from-json'; import ViewHeadlineIcon from '@mui/icons-material/ViewHeadline'; import ViewAgendaIcon from '@mui/icons-material/ViewAgenda'; +import { KeyboardArrowDown, KeyboardArrowRight } from '@mui/icons-material'; +import Collapse from '@mui/material/Collapse'; + import VulnerabilitiyCard from '../../Shared/VulnerabilityCard'; const useStyles = makeStyles((theme) => ({ @@ -107,6 +110,21 @@ const useStyles = makeStyles((theme) => ({ padding: '0.3rem', display: 'flex', justifyContent: 'center' + }, + dropdownArrowBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }, + dropdownText: { + color: '#1479FF', + fontSize: '1.5rem', + fontWeight: '600', + cursor: 'pointer', + textAlign: 'center' + }, + test: { + width: '95%' } })); @@ -119,8 +137,11 @@ function VulnerabilitiesDetails(props) { const abortController = useMemo(() => new AbortController(), []); const { name, tag, digest, platform } = props; + const [openExcludeSearch, setOpenExcludeSearch] = useState(false); + // pagination props const [cveFilter, setCveFilter] = useState(''); + const [cveExcludeFilter, setCveExcludeFilter] = useState(''); const [pageNumber, setPageNumber] = useState(1); const [isEndOfList, setIsEndOfList] = useState(false); const listBottom = useRef(null); @@ -140,7 +161,8 @@ function VulnerabilitiesDetails(props) { `${host()}${endpoints.vulnerabilitiesForRepo( getCVERequestName(), { pageNumber, pageSize: EXPLORE_PAGE_SIZE }, - cveFilter + cveFilter, + cveExcludeFilter )}`, abortController.signal ) @@ -224,7 +246,17 @@ function VulnerabilitiesDetails(props) { setAnchorExport(null); }; + const handleExpandCVESearch = () => { + setOpenExcludeSearch((openExcludeSearch) => !openExcludeSearch); + }; + + const handleCveExcludeFilterChange = (e) => { + const { value } = e.target; + setCveExcludeFilter(value); + }; + const debouncedChangeHandler = useMemo(() => debounce(handleCveFilterChange, 300)); + const debouncedExcludeFilterChangeHandler = useMemo(() => debounce(handleCveExcludeFilterChange, 300)); useEffect(() => { getPaginatedCVEs(); @@ -258,12 +290,13 @@ function VulnerabilitiesDetails(props) { useEffect(() => { if (isLoading) return; resetPagination(); - }, [cveFilter]); + }, [cveFilter, cveExcludeFilter]); useEffect(() => { return () => { abortController.abort(); debouncedChangeHandler.cancel(); + debouncedExcludeFilterChangeHandler.cancel(); }; }, []); @@ -364,15 +397,37 @@ function VulnerabilitiesDetails(props) { - - -
- + + +
+ {!openExcludeSearch ? ( + + ) : ( + + )}
+ + + +
+ +
+
+ + + + + + +
{renderCVEs()} {renderListBottom()} diff --git a/src/host.js b/src/host.js index 582b6ac4..18d5bf52 100644 --- a/src/host.js +++ b/src/host.js @@ -1,5 +1,5 @@ const hostConfig = { - auto: true, + auto: false, default: 'http://localhost:5000' };