Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
116 changes: 113 additions & 3 deletions frontend/src/components/plugins/tables/pluginActionsButtons.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
import { Button, Modal, ModalHeader, ModalBody } from "reactstrap";
import {
Button,
Modal,
ModalHeader,
ModalBody,
UncontrolledTooltip,
} from "reactstrap";
import { RiHeartPulseLine } from "react-icons/ri";
import { MdDelete, MdFileDownload, MdEdit } from "react-icons/md";
import {
MdDelete,
MdFileDownload,
MdEdit,
MdInfoOutline,
} from "react-icons/md";
import { BsPeopleFill } from "react-icons/bs";
import { AiFillSetting } from "react-icons/ai";
import { FaDiagramProject } from "react-icons/fa6";
import { VscJson } from "react-icons/vsc";
import { Link } from "react-router-dom";

import { IconButton } from "@certego/certego-ui";
import { IconButton, CustomJsonInput } from "@certego/certego-ui";

import { useAuthStore } from "../../../stores/useAuthStore";
import { useOrganizationStore } from "../../../stores/useOrganizationStore";
Expand Down Expand Up @@ -459,3 +472,100 @@ export function PlaybookFlowsButton({ playbook }) {
PlaybookFlowsButton.propTypes = {
playbook: PropTypes.object.isRequired,
};

export function MappingDataModel({ data, type, pythonModule }) {
// state
const [showModal, setShowModal] = React.useState(false);
const pythonModuleName = pythonModule.split(".")[0];

return (
<div className="d-flex flex-column align-items-center p-1">
<IconButton
id={`mapping-data-model__${pythonModuleName}`}
color="info"
size="sm"
Icon={VscJson}
title="View data model mapping"
onClick={() => setShowModal(!showModal)}
titlePlacement="top"
disabled={Object.keys(data).length === 0}
/>
{showModal && (
<Modal
id="mapping-data-model-modal"
autoFocus
centered
zIndex="1050"
size="lg"
keyboard={false}
backdrop="static"
labelledBy="Data model modal"
isOpen={showModal}
style={{ minWidth: "50%" }}
>
<ModalHeader className="mx-2" toggle={() => setShowModal(false)}>
<small className="text-info">
Data model mapping
<MdInfoOutline
id="dataModelMapping_infoicon"
fontSize="16"
className="ms-2"
/>
<UncontrolledTooltip
trigger="hover"
target="dataModelMapping_infoicon"
placement="right"
fade={false}
autohide={false}
innerClassName="p-2 text-start text-nowrap md-fit-content"
>
The main functionality of a `DataModel` is to model an
`Analyzer` result to a set of prearranged keys, allowing users
to easily search, evaluate and use the analyzer result.
<br />
For more info check the{" "}
<Link
to="https://intelowlproject.github.io/docs/IntelOwl/usage/#datamodels"
target="_blank"
>
official doc.
</Link>
</UncontrolledTooltip>
</small>
</ModalHeader>
<ModalBody className="d-flex flex-column mx-2">
<small>
The <strong className="text-info">keys </strong>
represent the path used to retrieve the value in the analyzer
report and the <strong className="text-info">value</strong> the
path of the data model.
</small>
<small>
For more info check the{" "}
<Link
to={`https://github.com/intelowlproject/IntelOwl/blob/master/api_app/analyzers_manager/${type}_analyzers/${pythonModuleName}.py`}
target="_blank"
>
analyzer&apos;s source code.
</Link>
</small>
<div className="my-2 d-flex justify-content-center">
<CustomJsonInput
id="data_model_mapping_json"
placeholder={data}
viewOnly
confirmGood={false}
/>
</div>
</ModalBody>
</Modal>
)}
</div>
);
}

MappingDataModel.propTypes = {
type: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
pythonModule: PropTypes.string.isRequired,
};
16 changes: 16 additions & 0 deletions frontend/src/components/plugins/tables/pluginTableColumns.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
PluginDeletionButton,
PluginConfigButton,
PlaybookFlowsButton,
MappingDataModel,
} from "./pluginActionsButtons";
import { JobTypes } from "../../../constants/jobConst";
import TableCell from "../../common/TableCell";
Expand Down Expand Up @@ -158,8 +159,23 @@ export const analyzersTableColumns = [
Cell: ({ value }) => <TLPTag value={value} />,
Filter: SelectOptionsFilter,
selectOptions: TlpChoices,
disableSortBy: true,
maxWidth: 80,
},
{
Header: "Data model",
id: "data_model",
accessor: (analyzerConfig) => analyzerConfig,
Cell: ({ value }) => (
<MappingDataModel
data={value.mapping_data_model}
type={value.type}
pythonModule={value.python_module}
/>
),
maxWidth: 70,
disableSortBy: true,
},
{
Header: "Actions",
id: "actions",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
PlaybooksEditButton,
PluginConfigButton,
PlaybookFlowsButton,
MappingDataModel,
} from "../../../../src/components/plugins/tables/pluginActionsButtons";
import {
mockedUseOrganizationStoreOwner,
Expand Down Expand Up @@ -485,3 +486,59 @@ describe("PlaybookFlowsButton test", () => {
});
});
});

describe("DataModel mapping test", () => {
test("DataModel mapping button", async () => {
const userAction = userEvent.setup();
const data = {
mapping_data_model: {
permalink: "external_references",
"data.hostnames": "resolutions",
},
type: "observable",
python_module: "pythonmodule.pythonclass",
};
const { container } = render(
<BrowserRouter>
<MappingDataModel
data={data.mapping_data_model}
type={data.type}
pythonModule={data.python_module}
/>
</BrowserRouter>,
);

const dataModelMappingIcon = container.querySelector(
"#mapping-data-model__pythonmodule",
);
expect(dataModelMappingIcon).toBeInTheDocument();

userAction.click(dataModelMappingIcon);
await waitFor(() => {
expect(screen.getByText("Data model mapping")).toBeInTheDocument();
});
});

test("DataModel mapping button - disabled", async () => {
const data = {
mapping_data_model: {},
type: "observable",
python_module: "pythonmodule.pythonclass",
};
const { container } = render(
<BrowserRouter>
<MappingDataModel
data={data.mapping_data_model}
type={data.type}
pythonModule={data.python_module}
/>
</BrowserRouter>,
);

const dataModelMappingIcon = container.querySelector(
"#mapping-data-model__pythonmodule",
);
expect(dataModelMappingIcon).toBeInTheDocument();
expect(dataModelMappingIcon.className).toContain("disabled");
});
});
1 change: 1 addition & 0 deletions frontend/tests/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ export const mockedPlugins = {
run_hash: false,
run_hash_type: "",
not_supported_filetypes: [],
mapping_data_model: {},
params: {
query_type: {
type: "str",
Expand Down
Loading