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

Adjusted recent scans #2104

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions api_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,11 @@ def recent_scans(self, request):
@action(detail=False, methods=["post"])
def recent_scans_user(self, request):
limit = request.data.get("limit", 5)
if "is_sample" not in request.data:
raise ValidationError({"detail": "is_sample is required"})
jobs = (
Job.objects.filter(user__pk=request.user.pk)
.filter(is_sample=request.data["is_sample"])
.annotate_importance(request.user)
.order_by("-importance", "-finished_analysis_time")[:limit]
)
Expand Down
39 changes: 29 additions & 10 deletions frontend/src/components/scan/utils/RecentScans.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React, { useMemo } from "react";
import PropTypes from "prop-types";
import md5 from "md5";
import { useNavigate } from "react-router-dom";
import { Card, CardHeader, CardBody } from "reactstrap";
import { Card, CardHeader, CardBody, UncontrolledTooltip } from "reactstrap";
import { MdInfoOutline } from "react-icons/md";
import { DateHoverable, Loader } from "@certego/certego-ui";
import { useRecentScansStore } from "../../../stores/useRecentScansStore";
import { JobResultSections } from "../../../constants/miscConst";
Expand Down Expand Up @@ -102,7 +103,7 @@ export default function RecentScans({ classification, param }) {
recentScansUser,
recentScans,
fetchRecentScansUser,
fetchRecentscans,
fetchRecentScans,
] = useRecentScansStore((state) => [
state.loadingScansUser,
state.loadingScansInsertedAnalyzable,
Expand All @@ -111,7 +112,7 @@ export default function RecentScans({ classification, param }) {
state.recentScansUser,
state.recentScans,
state.fetchRecentScansUser,
state.fetchRecentscans,
state.fetchRecentScans,
]);

console.debug(
Expand All @@ -121,25 +122,27 @@ export default function RecentScans({ classification, param }) {
loadingScansInsertedAnalyzable,
);

const isSample = classification === JobTypes.FILE;

// file md5
const [fileMd5, setFileMd5] = React.useState("");
/* md5 computation takes lots of time for huge file:
this freezes the scan form ui (render this component), we need this caching.
*/
useMemo(() => {
if (classification === JobTypes.FILE && param) {
if (isSample && param) {
param.text().then((text) => setFileMd5(md5(text)));
}
}, [classification, param]);
}, [isSample, param]);

React.useEffect(() => {
fetchRecentScansUser();
}, [fetchRecentScansUser]);
fetchRecentScansUser(isSample);
}, [fetchRecentScansUser, isSample]);
console.debug("recentScansUser", recentScansUser);

React.useEffect(() => {
fetchRecentscans(fileMd5.length ? fileMd5 : md5(param));
}, [fetchRecentscans, fileMd5, param]);
fetchRecentScans(fileMd5.length ? fileMd5 : md5(param), isSample);
}, [fetchRecentScans, fileMd5, param, isSample]);
console.debug("recentScans", recentScans);

// remove duplicate job
Expand All @@ -157,7 +160,23 @@ export default function RecentScans({ classification, param }) {
render={() => (
<div>
<div className="d-flex justify-content-between my-4 align-items-end">
<h5 className="fw-bold mb-0">Recent Scans</h5>
<div className="d-flex justify-content-between align-items-center">
<h5 className="fw-bold mb-0 me-2">Recent Scans</h5>
<MdInfoOutline id="recentscans-info-icon" />
<UncontrolledTooltip
target="recentscans-info-icon"
placement="right"
fade={false}
innerClassName="p-2 text-start text-nowrap md-fit-content"
>
<ul>
<li>Observable: scans up to 14 days ago</li>
<li>File: scans up to 60 days ago</li>
</ul>
If no observable or file has been inserted, recent scans are
related to the user and not the organization
</UncontrolledTooltip>
</div>
<small className="mx-2 text-gray">
{allRecentScans?.length} total
</small>
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/stores/useRecentScansStore.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ export const useRecentScansStore = create((set, _get) => ({
recentScansError: null,
recentScansUser: [],
recentScans: [],
fetchRecentScansUser: async () => {
fetchRecentScansUser: async (isSample) => {
try {
set({ loadingScansUser: true });
const resp = await axios.post(JOB_RECENT_SCANS_USER);
const resp = await axios.post(JOB_RECENT_SCANS_USER, {
is_sample: isSample,
});
set({
recentScansUser: resp.data,
loadingScansUser: false,
Expand All @@ -22,10 +24,13 @@ export const useRecentScansStore = create((set, _get) => ({
set({ recentScansUserError: error, loadingScansUser: false });
}
},
fetchRecentscans: async (md5) => {
fetchRecentScans: async (md5, isSample) => {
const body = {};
body.md5 = md5;
if (isSample) body.max_temporal_distance = 60;
try {
set({ loadingScansInsertedAnalyzable: true });
const resp = await axios.post(JOB_RECENT_SCANS, { md5 });
const resp = await axios.post(JOB_RECENT_SCANS, body);
set({
recentScans: resp.data,
loadingScansInsertedAnalyzable: false,
Expand Down
33 changes: 29 additions & 4 deletions frontend/tests/components/scan/utils/RecentScans.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe("Recent Scans test", () => {
}
});

render(
const { container } = render(
<BrowserRouter>
<RecentScans classification="generic" param="" />
</BrowserRouter>,
Expand All @@ -60,14 +60,21 @@ describe("Recent Scans test", () => {
await waitFor(() => {
const recentScansTitle = screen.getByText("Recent Scans");
expect(recentScansTitle).toBeInTheDocument();
const recentScansInfoIcon = container.querySelector(
"#recentscans-info-icon",
);
expect(recentScansInfoIcon).toBeInTheDocument();
const recentScansTotal = screen.getByText("0 total");
expect(recentScansTotal).toBeInTheDocument();
const elementText = screen.getByText("No recent scans available");
expect(elementText).toBeInTheDocument();
});

// axios call
expect(axios.post.mock.calls[0]).toEqual([JOB_RECENT_SCANS_USER]);
expect(axios.post.mock.calls[0]).toEqual([
JOB_RECENT_SCANS_USER,
{ is_sample: false },
]);
expect(axios.post.mock.calls[1]).toEqual([
JOB_RECENT_SCANS,
{ md5: md5("") },
Expand Down Expand Up @@ -95,6 +102,10 @@ describe("Recent Scans test", () => {
await waitFor(() => {
const recentScansTitle = screen.getByText("Recent Scans");
expect(recentScansTitle).toBeInTheDocument();
const recentScansInfoIcon = container.querySelector(
"#recentscans-info-icon",
);
expect(recentScansInfoIcon).toBeInTheDocument();
const recentScansTotal = screen.getByText("1 total");
expect(recentScansTotal).toBeInTheDocument();
// card (observable and no playbook)
Expand All @@ -115,7 +126,10 @@ describe("Recent Scans test", () => {
});

// axios call
expect(axios.post.mock.calls[0]).toEqual([JOB_RECENT_SCANS_USER]);
expect(axios.post.mock.calls[0]).toEqual([
JOB_RECENT_SCANS_USER,
{ is_sample: false },
]);
expect(axios.post.mock.calls[1]).toEqual([
JOB_RECENT_SCANS,
{ md5: md5("") },
Expand Down Expand Up @@ -143,6 +157,10 @@ describe("Recent Scans test", () => {
await waitFor(() => {
const recentScansTitle = screen.getByText("Recent Scans");
expect(recentScansTitle).toBeInTheDocument();
const recentScansInfoIcon = container.querySelector(
"#recentscans-info-icon",
);
expect(recentScansInfoIcon).toBeInTheDocument();
const recentScansTotal = screen.getByText("2 total");
expect(recentScansTotal).toBeInTheDocument();

Expand Down Expand Up @@ -178,7 +196,10 @@ describe("Recent Scans test", () => {
});

// axios call
expect(axios.post.mock.calls[0]).toEqual([JOB_RECENT_SCANS_USER]);
expect(axios.post.mock.calls[0]).toEqual([
JOB_RECENT_SCANS_USER,
{ is_sample: false },
]);
expect(axios.post.mock.calls[1]).toEqual([
JOB_RECENT_SCANS,
{ md5: md5("1.2.3.4") },
Expand Down Expand Up @@ -208,6 +229,10 @@ describe("Recent Scans test", () => {
await waitFor(() => {
const recentScansTitle = screen.getByText("Recent Scans");
expect(recentScansTitle).toBeInTheDocument();
const recentScansInfoIcon = container.querySelector(
"#recentscans-info-icon",
);
expect(recentScansInfoIcon).toBeInTheDocument();
const recentScansTotal = screen.getByText("1 total");
expect(recentScansTotal).toBeInTheDocument();

Expand Down
4 changes: 3 additions & 1 deletion tests/api_app/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,9 @@ def test_recent_scan_user(self):
"finished_analysis_time": now() - datetime.timedelta(hours=2),
}
)
response = self.client.post(self.jobs_recent_scans_user_uri)
response = self.client.post(
self.jobs_recent_scans_user_uri, data={"is_sample": False}
)
content = response.json()
msg = (response, content)
self.assertEqual(200, response.status_code, msg=msg)
Expand Down
Loading