Reconmap/web-client

View on GitHub
src/components/vulnerabilities/VulnerabilitiesTable.jsx

Summary

Maintainability
C
1 day
Test Coverage
import { Stack } from "@chakra-ui/react";
import NativeCheckbox from "components/form/NativeCheckbox";
import RestrictedComponent from "components/logic/RestrictedComponent";
import ProjectBadge from "components/projects/ProjectBadge";
import AscendingSortLink from "components/ui/AscendingSortLink";
import DescendingSortLink from "components/ui/DescendingSortLink";
import Tags from "components/ui/Tags";
import DeleteIconButton from "components/ui/buttons/DeleteIconButton";
import ReloadButton from "components/ui/buttons/Reload";
import LoadingTableRow from "components/ui/tables/LoadingTableRow";
import NoResultsTableRow from "components/ui/tables/NoResultsTableRow";
import useDelete from "hooks/useDelete";
import CvssScore from "../badges/CvssScore";
import RiskBadge from "../badges/RiskBadge";
import VulnerabilityBadge from "../badges/VulnerabilityBadge";
import LinkButton from "../ui/buttons/Link";
import VulnerabilityStatusBadge from "./StatusBadge";
import VulnerabilityCategorySpan from "./categories/Span";

const VulnerabilitiesTable = ({
    tableModel,
    tableModelSetter: setTableModel,
    reloadCallback,
    showSelection = true,
    showProjectColumn = true,
}) => {
    const onSortChange = (ev, column, order) => {
        ev.preventDefault();

        setTableModel({
            ...tableModel,
            sortBy: { column: column, order: order },
        });
    };

    const onSelectionChange = (ev) => {
        const target = ev.target;
        const selectionId = parseInt(target.value);
        if (target.checked) {
            setTableModel({
                ...tableModel,
                selection: [...tableModel.selection, selectionId],
            });
        } else {
            setTableModel({
                ...tableModel,
                selection: tableModel.selection.filter(
                    (value) => value !== selectionId,
                ),
            });
        }
    };

    const onHeaderCheckboxClick = (ev) => {
        if (ev.target.checked) {
            setTableModel({
                ...tableModel,
                selection: tableModel.vulnerabilities.map(
                    (vulnerability) => vulnerability.id,
                ),
            });
        } else {
            setTableModel({ ...tableModel, selection: [] });
        }
    };

    const numColumns =
        6 + (showSelection ? 1 : 0) + (showProjectColumn ? 1 : 0);
    const vulnerabilitiesLength =
        null !== tableModel.vulnerabilities
            ? tableModel.vulnerabilities.length
            : 0;

    const deleteVulnerability = useDelete(
        "/vulnerabilities/",
        reloadCallback,
        "Do you really want to delete this vulnerability?",
        "The vulnerability has been deleted.",
    );

    return (
        <table className="rm-listing">
            <thead>
                <tr>
                    {showSelection && (
                        <th style={{ width: "32px" }}>
                            <NativeCheckbox
                                onChange={onHeaderCheckboxClick}
                                checked={
                                    tableModel.selection.length &&
                                    tableModel.selection.length ===
                                        vulnerabilitiesLength
                                }
                                disabled={
                                    tableModel.vulnerabilitiesLength === 0
                                }
                            />
                        </th>
                    )}
                    <th style={{ width: "190px" }}>Summary</th>
                    {showProjectColumn && (
                        <th style={{ width: "190px" }}>Project</th>
                    )}
                    <th style={{ width: "120px" }}>
                        <DescendingSortLink
                            callback={onSortChange}
                            property="status"
                        />{" "}
                        Status{" "}
                        <AscendingSortLink
                            callback={onSortChange}
                            property="status"
                        />
                    </th>
                    <th style={{ width: "120px" }}>
                        <DescendingSortLink
                            callback={onSortChange}
                            property="risk"
                        />{" "}
                        Risk{" "}
                        <AscendingSortLink
                            callback={onSortChange}
                            property="risk"
                        />
                    </th>
                    <th style={{ width: "70px" }}>
                        <DescendingSortLink
                            callback={onSortChange}
                            property="cvss_score"
                        />{" "}
                        <abbr title="Common Vulnerability Scoring System">
                            CVSS
                        </abbr>{" "}
                        score{" "}
                        <AscendingSortLink
                            callback={onSortChange}
                            property="cvss_score"
                        />
                    </th>
                    <th className="only-desktop" style={{ width: "20%" }}>
                        <DescendingSortLink
                            callback={onSortChange}
                            property="category_name"
                        />{" "}
                        Category{" "}
                        <AscendingSortLink
                            callback={onSortChange}
                            property="category_name"
                        />
                    </th>
                    <th style={{ width: "15%", textAlign: "right" }}>
                        <ReloadButton onClick={reloadCallback} />
                    </th>
                </tr>
            </thead>
            <tbody>
                {null === tableModel.vulnerabilities && (
                    <LoadingTableRow numColumns={numColumns} />
                )}
                {null !== tableModel.vulnerabilities &&
                    0 === tableModel.vulnerabilities.length && (
                        <NoResultsTableRow numColumns={numColumns} />
                    )}
                {null !== tableModel.vulnerabilities &&
                    tableModel.vulnerabilities.length > 0 &&
                    tableModel.vulnerabilities.map((vulnerability, index) => {
                        return (
                            <tr key={index}>
                                {showSelection && (
                                    <td>
                                        <NativeCheckbox
                                            value={vulnerability.id}
                                            onChange={onSelectionChange}
                                            checked={tableModel.selection.includes(
                                                vulnerability.id,
                                            )}
                                        />
                                    </td>
                                )}
                                <td>
                                    <Stack>
                                        <VulnerabilityBadge
                                            vulnerability={vulnerability}
                                        />
                                        <div>
                                            <Tags values={vulnerability.tags} />
                                        </div>
                                    </Stack>
                                </td>
                                {showProjectColumn && (
                                    <td>
                                        {vulnerability.is_template ? (
                                            <span title="Not applicable">
                                                (n/a)
                                            </span>
                                        ) : (
                                            <ProjectBadge
                                                project={{
                                                    id: vulnerability.project_id,
                                                    name: vulnerability.project_name,
                                                }}
                                            />
                                        )}
                                    </td>
                                )}
                                <td>
                                    <VulnerabilityStatusBadge
                                        vulnerability={vulnerability}
                                    />
                                </td>
                                <td>
                                    <RiskBadge risk={vulnerability.risk} />
                                </td>
                                <td>
                                    <CvssScore
                                        score={vulnerability.cvss_score}
                                    />
                                </td>
                                <td className="only-desktop">
                                    <VulnerabilityCategorySpan
                                        name={vulnerability.category_name}
                                        parentName={
                                            vulnerability.parent_category_name
                                        }
                                    />
                                </td>
                                <td textAlign="right">
                                    <RestrictedComponent
                                        roles={[
                                            "administrator",
                                            "superuser",
                                            "user",
                                        ]}
                                    >
                                        <LinkButton
                                            href={`/vulnerabilities/${vulnerability.id}/edit`}
                                        >
                                            Edit
                                        </LinkButton>
                                        {reloadCallback && (
                                            <DeleteIconButton
                                                onClick={() =>
                                                    deleteVulnerability(
                                                        vulnerability.id,
                                                    )
                                                }
                                            />
                                        )}
                                    </RestrictedComponent>
                                </td>
                            </tr>
                        );
                    })}
            </tbody>
        </table>
    );
};

export default VulnerabilitiesTable;