Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI v2 new theme vuln scan results #1231

Merged
merged 42 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
50d3f54
wip
manV Jun 13, 2023
99a5311
reset page number on page resize
manV Jun 13, 2023
71afcdf
update table and breadcrumb
milan-deepfence Jun 13, 2023
51bb044
added block style
milan-deepfence Jun 13, 2023
21444ff
update scan status component
milan-deepfence Jun 13, 2023
d0cf6e2
fix button start and end icon
milan-deepfence Jun 13, 2023
e572026
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 13, 2023
f61c618
remove unecessary import
milan-deepfence Jun 13, 2023
686e070
Merge branch 'ui-v2-new-theme' into ui-v2-new-theme-vuln-scan-results
manV Jun 13, 2023
3e892ca
update delete modal
milan-deepfence Jun 13, 2023
d3fe624
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 14, 2023
41d79c6
add filters for scan results page
manV Jun 15, 2023
787c7c1
fix bulk actions for vulnerability scan results
manV Jun 15, 2023
d6c210a
added search in filters
milan-deepfence Jun 15, 2023
96c1dc3
added keep previous data
milan-deepfence Jun 15, 2023
713861d
add loader for combobox, add no results found
milan-deepfence Jun 15, 2023
eecfcd1
added unique vuln, update static filter
milan-deepfence Jun 15, 2023
b61e667
updated vulnerability details component
manV Jun 16, 2023
6bf437d
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 16, 2023
9214904
added most exploitable page
milan-deepfence Jun 16, 2023
2dba2a7
Merge branch 'ui-v2-new-theme-vuln-scan-results' of https://github.co…
milan-deepfence Jun 16, 2023
41b0918
added filter count
milan-deepfence Jun 16, 2023
db6a4b5
process labels for vulnerability details modal
manV Jun 16, 2023
5a067e9
added new theme for malware and secret scans list
milan-deepfence Jun 16, 2023
ec01d38
add secrets and malware details
milan-deepfence Jun 16, 2023
2416932
wip scan history dropdown
milan-deepfence Jun 16, 2023
9461efe
change vulnerability landing page with new theme
manV Jun 19, 2023
f23c5d9
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 19, 2023
19d80ee
increase options column size
manV Jun 19, 2023
1c237fb
added scan history dropdown, fixes statuses and other icons
milan-deepfence Jun 19, 2023
bdb8b73
Merge branch 'ui-v2-new-theme-vuln-scan-results' of https://github.co…
milan-deepfence Jun 19, 2023
c99026e
fix graph background and toolbar styles
manV Jun 19, 2023
77f38b7
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 19, 2023
6d9f8aa
add missing outlets for unique and most exploitable vulnerabilities
manV Jun 19, 2023
a107f0f
added registries landing cards
milan-deepfence Jun 19, 2023
5b72214
Merge branch 'ui-v2-new-theme-vuln-scan-results' of https://github.co…
milan-deepfence Jun 19, 2023
34fb2c7
adjust skeleton padding for registry card
milan-deepfence Jun 19, 2023
0b88ee1
update react query for registry summary
milan-deepfence Jun 19, 2023
1c745c1
fix top 5 vulnerabilities for scan and other fixes
manV Jun 19, 2023
207401a
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 19, 2023
257c965
added missing suspense for registry summary
milan-deepfence Jun 19, 2023
0c03af7
Merge branch 'ui-v2-new-theme-vuln-scan-results' of github.com:deepfe…
manV Jun 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function AppHeader() {
</>
}
>
<button className="dark:text-text-text-and-icon flex gap-[6px] items-center">
<button className="dark:text-text-text-and-icon flex gap-[6px] items-center text-p1">
<div className="h-[18px] w-[18px]">
<UserLine />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { MdCopyAll } from 'react-icons/md';
import { useCopyToClipboard } from 'react-use';
import { twMerge } from 'tailwind-merge';

/**
* @deprecated
*/
export const CopyToClipboard = ({
data,
className,
Expand Down Expand Up @@ -72,3 +75,29 @@ export const CopyToClipboard = ({
</button>
);
};

export function useCopyToClipboardState() {
const [_, copyToClipboard] = useCopyToClipboard();
const [isCopied, setIsCopied] = useState(false);
const timeoutIdRef = useRef<string | null>();

useEffect(() => {
return () => {
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
};
}, []);

return {
copy: (data: string) => {
if (timeoutIdRef.current) {
clearTimeout(timeoutIdRef.current);
}
copyToClipboard(data);
setIsCopied(true);
timeoutIdRef.current = setTimeout(() => {
setIsCopied(false);
}, 5000) as unknown as string;
},
isCopied: isCopied,
};
}
39 changes: 24 additions & 15 deletions deepfence_frontend/apps/dashboard/src/components/DFLink.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
import { ComponentPropsWithRef, forwardRef } from 'react';
import { Link, LinkProps } from 'react-router-dom';
import { twMerge } from 'tailwind-merge';
import { cn } from 'tailwind-preset';

const baseClassName =
'text-blue-600 dark:text-blue-500 hover:underline focus:underline visited:text-purple-600 dark:visited:text-purple-500';
'text-blue-600 dark:text-text-link hover:underline focus:underline visited:text-purple-600 dark:visited:text-purple-500';

type AnchorProps = React.ComponentPropsWithRef<'a'>;
type AnchorProps = React.ComponentPropsWithRef<'a'> & {
unstyled?: boolean;
};
const AAnchor = forwardRef<HTMLAnchorElement, AnchorProps>(
({ children, className, ...props }, ref) => (
<a {...props} className={twMerge(baseClassName, className)} ref={ref}>
({ children, className, unstyled, ...props }, ref) => (
<a {...props} className={cn(unstyled ? '' : baseClassName, className)} ref={ref}>
{children}
</a>
),
);
AAnchor.displayName = 'DFLink.AAnchor';

const ALink = forwardRef<HTMLAnchorElement, LinkProps>(
({ children, className, ...props }, ref) => (
<Link {...props} className={twMerge(baseClassName, className)} ref={ref}>
{children}
</Link>
),
);
const ALink = forwardRef<
HTMLAnchorElement,
LinkProps & {
unstyled?: boolean;
}
>(({ children, className, unstyled, ...props }, ref) => (
<Link {...props} className={cn(unstyled ? '' : baseClassName, className)} ref={ref}>
{children}
</Link>
));
ALink.displayName = 'DFLink.AAnchor';

type DFLinkProps = ComponentPropsWithRef<typeof AAnchor | typeof ALink>;
type DFLinkProps = ComponentPropsWithRef<
(typeof AAnchor | typeof ALink) & {
unstyled?: boolean;
}
>;
export const DFLink = forwardRef<HTMLAnchorElement, DFLinkProps>(
(props: DFLinkProps, ref) => {
return 'href' in props ? (
<AAnchor {...(props as AnchorProps)} ref={ref} />
<AAnchor {...(props as AnchorProps)} unstyled={props.unstyled} ref={ref} />
) : (
<ALink {...(props as LinkProps)} ref={ref} />
<ALink {...(props as LinkProps)} unstyled={props.unstyled} ref={ref} />
);
},
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,74 @@
import cx from 'classnames';
import { Badge } from 'ui-components';
import { capitalize } from 'lodash-es';
import { cn } from 'tailwind-preset';
import { CircleSpinner } from 'ui-components';

import {
ErrorIcon,
NotStartedIcon,
SuccessIcon,
} from '@/components/icons/common/ScanStatuses';
import {
isNeverScanned,
isScanComplete,
isScanFailed,
isScanInProgress,
} from '@/utils/scan';

export const ScanStatusBadge = ({ status }: { status: string }) => {
return (
<Badge
label={(isNeverScanned(status) ? 'NEVER_SCANNED' : status).replaceAll('_', ' ')}
className={cx('max-w-full w-fit truncate', {
'bg-green-100 dark:bg-green-600/10 text-green-600 dark:text-green-400':
isScanComplete(status),
'bg-red-100 dark:bg-red-600/10 text-red-600 dark:text-red-400':
isScanFailed(status),
'bg-blue-100 dark:bg-blue-600/10 text-blue-600 dark:text-blue-400':
isScanInProgress(status),
'bg-gray-100 dark:bg-gray-600/10 text-gray-600 dark:text-gray-400':
isNeverScanned(status),
})}
size="sm"
/>
export const ScanStatusBadge = ({
status,
className,
justIcon = false,
}: {
status: string;
className?: string;
justIcon?: boolean;
}) => {
const wrapperClassName = cn(
'flex items-center gap-1.5 dark:text-text-text-and-icon text-p4',
className,
);

const iconWrapper = cn('w-[18px] h-[18px]');

const scanStatus = capitalize(status.replaceAll('_', ' '));

if (isScanComplete(status)) {
return (
<div className={wrapperClassName}>
<span className={iconWrapper}>
<SuccessIcon />
</span>

{!justIcon ? scanStatus : null}
</div>
);
} else if (isScanFailed(status)) {
return (
<div className={wrapperClassName}>
<span className={iconWrapper}>
<ErrorIcon />
</span>
{!justIcon ? scanStatus : null}
</div>
);
} else if (isNeverScanned(status)) {
return (
<div className={wrapperClassName}>
<span className={iconWrapper}>
<NotStartedIcon />
</span>
Never Scanned
</div>
);
} else if (isScanInProgress(status)) {
return (
<div className={wrapperClassName}>
<span className={iconWrapper}>
<CircleSpinner size="sm" />
</span>
{!justIcon ? scanStatus : null}
</div>
);
}
return null;
};
139 changes: 77 additions & 62 deletions deepfence_frontend/apps/dashboard/src/components/SeverityBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,87 @@
export const SeverityBadge = ({ severity }: { severity: string }) => {
import { cn } from 'tailwind-preset';

import { SeverityScoreIcon } from '@/components/icons/common/SeverityScore';
import { getColorForCVSSScore } from '@/constants/charts';

export const SeverityBadge = ({
severity,
className,
}: {
severity: string;
className?: string;
}) => {
return (
<div className="flex items-center capitalize text-p4 dark:text-text-text-and-icon gap-[6px]">
<div className="h-[18px] w-[18px]">
<SeverityIcon severity={severity} />
</div>{' '}
<div
className={cn(
'flex items-center capitalize justify-center font-semibold leading-4 text-[11px] dark:text-text-text-inverse py-0.5 max-w-[62px] min-w-[62px]',
'dark:bg-df-gray-500 rounded-[5px]',
{
'dark:bg-status-error': severity === 'critical',
'dark:bg-chart-orange': severity === 'high',
'dark:bg-status-warning': severity === 'medium',
'dark:bg-chart-yellow1': severity === 'low',
},
className,
)}
>
{severity}
</div>
);
};

const SeverityIcon = ({ severity }: { severity: string }) => {
const severities = ['unknown', 'low', 'medium', 'high', 'critical'];
const activeFillClassName =
{
critical: 'dark:fill-status-error',
high: 'dark:fill-chart-orange',
medium: 'dark:fill-status-warning',
low: 'dark:fill-chart-yellow1',
}[severity] ?? '';
const defaultFillClassName = 'dark:fill-df-gray-700';
export const CveCVSSScore = ({
score,
className,
iconClassName,
}: {
score?: number;
className?: string;
iconClassName?: string;
}) => {
return (
<div
className={cn('flex gap-1 items-center text-p3', className)}
style={{
color: getColorForCVSSScore(score),
}}
>
<div className={cn('h-[16px] w-[16px] shrink-0', iconClassName)}>
<SeverityScoreIcon />
</div>
<div>{score?.toFixed(1) ?? '—'}</div>
</div>
);
};

export const SeverityLegend = ({
severity,
className,
iconClassName,
}: {
severity: string;
className?: string;
iconClassName?: string;
}) => {
return (
<svg
width="100%"
height="100%"
viewBox="0 0 17 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
<div
className={cn(
'flex items-center gap-2 flex-nowrap text-p7 dark:text-text-text-and-icon capitalize',
className,
)}
>
<rect
x="1.23389"
y="12"
width="2"
height="3"
rx="1"
className={
severities.indexOf(severity) > 0 ? activeFillClassName : defaultFillClassName
}
/>
<rect
x="5.23389"
y="9"
width="2"
height="6"
rx="1"
className={
severities.indexOf(severity) > 1 ? activeFillClassName : defaultFillClassName
}
/>
<rect
x="9.23389"
y="6"
width="2"
height="9"
rx="1"
className={
severities.indexOf(severity) > 2 ? activeFillClassName : defaultFillClassName
}
/>
<rect
x="13.2339"
y="3"
width="2"
height="12"
rx="1"
className={
severities.indexOf(severity) > 3 ? activeFillClassName : defaultFillClassName
}
/>
</svg>
<div
className={cn(
'rounded-full h-3 w-3 shrink-0 dark:bg-df-gray-500',
{
'dark:bg-status-error': severity === 'critical',
'dark:bg-chart-orange': severity === 'high',
'dark:bg-status-warning': severity === 'medium',
'dark:bg-chart-yellow1': severity === 'low',
},
iconClassName,
)}
></div>
<div>{severity}</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TimesIcon } from '@/components/icons/common/Times';

export const FilterBadge = ({
text,
onRemove,
}: {
text: string;
onRemove: () => void;
}) => {
return (
<div className="flex items-center py-1 px-2.5 dark:border dark:border-accent-accent rounded-[11px] gap-2">
<div className="text-p8 dark:text-text-input-value truncate max-w-[150px]">
{text}
</div>
<button
className="h-3.5 w-3.5 dark:text-text-input-value"
onClick={() => {
onRemove();
}}
>
<TimesIcon />
</button>
</div>
);
};
Loading