mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Host Details Page: Render vulnerable software warnings (#1220)
* Conditionally renders vulnerability div and issue tooltip * Refactors IconToolTip to include an Error icon * Add vulnerabilities to type interface, small cleanup Co-authored-by: Rachel Elysia Perkins <rachel@fleetdm.com>
This commit is contained in:
parent
f1e42ee775
commit
d6d584094f
BIN
assets/images/icon-issue-fleet-black-50-16x16@2x.png
Normal file
BIN
assets/images/icon-issue-fleet-black-50-16x16@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 917 B |
@ -4,14 +4,13 @@ import ReactTooltip from "react-tooltip";
|
||||
interface IIconToolTipProps {
|
||||
text: string;
|
||||
isHtml?: boolean;
|
||||
issue?: boolean;
|
||||
}
|
||||
|
||||
// TODO: handle html text better. possibly use 'children' prop for html
|
||||
const IconToolTip = (props: IIconToolTipProps): JSX.Element => {
|
||||
const { text, isHtml } = props;
|
||||
return (
|
||||
<div className="icon-tooltip">
|
||||
<span data-tip={text} data-html={isHtml}>
|
||||
const { text, isHtml, issue } = props;
|
||||
let svgIcon = (
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
@ -25,6 +24,28 @@ const IconToolTip = (props: IIconToolTipProps): JSX.Element => {
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
if (issue) {
|
||||
svgIcon = (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M0 8C0 12.4183 3.5817 16 8 16C12.4183 16 16 12.4183 16 8C16 3.5817 12.4183 0 8 0C3.5817 0 0 3.5817 0 8ZM14 8C14 11.3137 11.3137 14 8 14C4.6863 14 2 11.3137 2 8C2 4.6863 4.6863 2 8 2C11.3137 2 14 4.6863 14 8ZM7 12V10H9V12H7ZM7 4V9H9V4H7Z"
|
||||
fill="#8B8FA2"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="icon-tooltip">
|
||||
<span data-tip={text} data-html={isHtml}>
|
||||
{svgIcon}
|
||||
</span>
|
||||
{/* same colour as $core-fleet-blue */}
|
||||
<ReactTooltip
|
||||
|
@ -5,4 +5,13 @@ export default PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
version: PropTypes.string,
|
||||
id: PropTypes.number,
|
||||
vulnerabilities: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
uid: PropTypes.number,
|
||||
username: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
groupname: PropTypes.string,
|
||||
})
|
||||
),
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ import Button from "components/buttons/Button";
|
||||
import Modal from "components/modals/Modal";
|
||||
import SoftwareListRow from "pages/hosts/HostDetailsPage/SoftwareListRow";
|
||||
import PackQueriesListRow from "pages/hosts/HostDetailsPage/PackQueriesListRow";
|
||||
import SoftwareVulnerabilities from "pages/hosts/HostDetailsPage/SoftwareVulnerabilities";
|
||||
import HostUsersListRow from "pages/hosts/HostDetailsPage/HostUsersListRow";
|
||||
|
||||
import permissionUtils from "utilities/permissions";
|
||||
@ -377,6 +378,7 @@ export class HostDetailsPage extends Component {
|
||||
return (
|
||||
<div className="section section--software">
|
||||
<p className="section__header">Software</p>
|
||||
|
||||
{host.software.length === 0 ? (
|
||||
<div className="results">
|
||||
<p className="results__header">
|
||||
@ -388,18 +390,20 @@ export class HostDetailsPage extends Component {
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<SoftwareVulnerabilities softwareList={host.software} />
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
<table className={wrapperClassName}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Installed Version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!!host.software.length &&
|
||||
host.software.map((software) => {
|
||||
{host.software.map((software) => {
|
||||
return (
|
||||
<SoftwareListRow
|
||||
key={`software-row-${software.id}`}
|
||||
@ -410,6 +414,7 @@ export class HostDetailsPage extends Component {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { Component } from "react";
|
||||
import IconToolTip from "components/IconToolTip";
|
||||
|
||||
import softwareInterface from "interfaces/software";
|
||||
|
||||
@ -11,7 +12,7 @@ class SoftwareListRow extends Component {
|
||||
|
||||
render() {
|
||||
const { software } = this.props;
|
||||
const { name, source, version } = software;
|
||||
const { name, source, version, vulnerabilities } = software;
|
||||
|
||||
const TYPE_CONVERSION = {
|
||||
apt_sources: "Package (APT)",
|
||||
@ -35,8 +36,26 @@ class SoftwareListRow extends Component {
|
||||
|
||||
const type = TYPE_CONVERSION[source] || "Unknown";
|
||||
|
||||
const vulnerabilitiesIcon = () => {
|
||||
if (vulnerabilities.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const vulText =
|
||||
vulnerabilities.length === 1 ? "vulnerability" : "vulnerabilities";
|
||||
|
||||
return (
|
||||
<IconToolTip
|
||||
text={`${vulnerabilities.length} ${vulText} detected`}
|
||||
issue
|
||||
isHtml
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td className={`${baseClass}__name`}>{vulnerabilitiesIcon()}</td>
|
||||
<td className={`${baseClass}__name`}>{name}</td>
|
||||
<td className={`${baseClass}__type`}>{type}</td>
|
||||
<td className={`${baseClass}__installed-version`}>{version}</td>
|
||||
|
@ -0,0 +1,86 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import FleetIcon from "components/icons/FleetIcon";
|
||||
import softwareInterface from "interfaces/software";
|
||||
|
||||
const baseClass = "software-vulnerabilities";
|
||||
|
||||
class SoftwareVulnerabilities extends Component {
|
||||
static propTypes = {
|
||||
softwareList: PropTypes.arrayOf(softwareInterface),
|
||||
};
|
||||
|
||||
render() {
|
||||
const { softwareList } = this.props;
|
||||
|
||||
const vulsList = [];
|
||||
|
||||
const vulnerabilitiesListMaker = () => {
|
||||
softwareList.forEach((software) => {
|
||||
if (software.vulnerabilities) {
|
||||
software.vulnerabilities.forEach((vulnerability) => {
|
||||
vulsList.push({
|
||||
name: software.name,
|
||||
cve: vulnerability.cve,
|
||||
details_link: vulnerability.details_link,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
vulnerabilitiesListMaker();
|
||||
|
||||
const renderVulsCount = (list) => {
|
||||
if (list.length === 1) {
|
||||
return "1 vulnerability detected";
|
||||
}
|
||||
return `${list.length} vulnerabilities detected`;
|
||||
};
|
||||
|
||||
const renderVul = (vul, index) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
Read more about{" "}
|
||||
<a href={vul.details_link} target="_blank" rel="noopener noreferrer">
|
||||
<em>{vul.name}</em> {vul.cve} vulnerability
|
||||
<FleetIcon name="external-link" />
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
// No software vulnerabilities
|
||||
if (vulsList.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Software vulnerabilities
|
||||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className={`${baseClass}__count`}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M0 8C0 12.4183 3.5817 16 8 16C12.4183 16 16 12.4183 16 8C16 3.5817 12.4183 0 8 0C3.5817 0 0 3.5817 0 8ZM14 8C14 11.3137 11.3137 14 8 14C4.6863 14 2 11.3137 2 8C2 4.6863 4.6863 2 8 2C11.3137 2 14 4.6863 14 8ZM7 12V10H9V12H7ZM7 4V9H9V4H7Z"
|
||||
fill="#8B8FA2"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{renderVulsCount(vulsList)}
|
||||
</div>
|
||||
<div className={`${baseClass}__list`}>
|
||||
<ul>{vulsList.map((vul, index) => renderVul(vul, index))}</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SoftwareVulnerabilities;
|
@ -0,0 +1,26 @@
|
||||
.software-vulnerabilities {
|
||||
font-size: $x-small;
|
||||
background-color: $ui-off-white;
|
||||
border: solid 1px $ui-fleet-black-50;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10px;
|
||||
overflow: scroll;
|
||||
margin-bottom: $pad-large;
|
||||
padding: $pad-large;
|
||||
padding-bottom: $pad-small;
|
||||
|
||||
a {
|
||||
color: $core-vibrant-blue;
|
||||
font-weight: $bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&__count {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
}
|
||||
|
||||
svg {
|
||||
padding-right: $pad-xxsmall;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { default } from "./SoftwareVulnerabilities";
|
@ -307,6 +307,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.section--software {
|
||||
th {
|
||||
&:first-child {
|
||||
border-right: none;
|
||||
width: 16px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td {
|
||||
&:first-child {
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
td {
|
||||
padding: 12px 27px;
|
||||
|
Loading…
Reference in New Issue
Block a user