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

Bump werkzeug from 3.0.2 to 3.0.3 in /app #83

Closed
wants to merge 29 commits into from
Closed
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1469bc6
Updating config.js
Jan 29, 2024
4c51ddd
Adding QR Card
phutelmyer Feb 2, 2024
281508d
Merge pull request #68 from target/qr-card-update
phutelmyer Feb 2, 2024
9cd403d
Update EventNode.js
phutelmyer Feb 2, 2024
dfa8e2c
Update api_examples.py
phutelmyer Feb 21, 2024
6e88ff7
Update strelka.py
phutelmyer Feb 22, 2024
6cb843a
Adding TLSH and QR Fix
phutelmyer Mar 4, 2024
749487e
Updating placeholder due to more search capability
phutelmyer Mar 4, 2024
c3e2640
Bug fix for missing key
phutelmyer Mar 4, 2024
2bb0af8
Merge pull request #73 from target/tlsh-update
phutelmyer Mar 4, 2024
34dba0f
Updating dependencies
phutelmyer Mar 20, 2024
1808ccb
Merge pull request #75 from target/dependency-updates
phutelmyer Mar 20, 2024
52a2542
VT Update
phutelmyer Mar 29, 2024
98c1a8f
Merge pull request #79 from target/unencrypted-vt-support
phutelmyer Mar 29, 2024
92eaa70
Bump black from 21.12b0 to 24.3.0 in /app
dependabot[bot] Mar 29, 2024
1376337
Update CHANGELOG.md
phutelmyer Mar 29, 2024
af4d4d6
Merge pull request #76 from target/dependabot/pip/app/black-24.3.0
phutelmyer Mar 29, 2024
e373dc9
Update layoutUtils.js
phutelmyer Mar 29, 2024
773c11e
Fix if no VT key
phutelmyer Mar 29, 2024
c4873a1
Fix for missing API key
phutelmyer Mar 29, 2024
730121d
Adding Backend VirusTotal Augment Route
phutelmyer Apr 19, 2024
eb73c85
Updating Backend Dependencies
phutelmyer Apr 19, 2024
cb86a67
Updating dependencies
phutelmyer Apr 19, 2024
f23a3c7
Updating config / removing VT API key
phutelmyer Apr 19, 2024
cf4f323
Refactoring Cards
phutelmyer Apr 19, 2024
30dcea1
Dependency bumps
phutelmyer Apr 19, 2024
a11aef9
Merge pull request #81 from target/vt-augment-and-additional-updates
phutelmyer Apr 19, 2024
4105640
Updating README images
phutelmyer Apr 19, 2024
e5e7164
Bump werkzeug from 3.0.2 to 3.0.3 in /app
dependabot[bot] May 6, 2024
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
47 changes: 36 additions & 11 deletions ui/src/components/FileComponents/OcrOverviewCard.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import React, { useState } from "react";
import { Checkbox, Input, Row, Col, Modal } from "antd";
import { Checkbox, Input, Row, Col, Modal, Button, Tooltip } from "antd";
import "../../styles/OcrOverviewCard.css";

const OcrOverviewCard = ({ data }) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [wrapText, setWrapText] = useState(false);
const [trimText, setTrimText] = useState(true);
const [filter, setFilter] = useState("");
const [isBlurred, setIsBlurred] = useState(data.scan.qr ? true : false); // State to manage blur for QR codes

const showModal = () => setIsModalVisible(true);
const showModal = () => {
// Only show modal if the image is not blurred
if (!isBlurred) {
setIsModalVisible(true);
}
};
const handleCancel = () => setIsModalVisible(false);
const toggleBlur = () => setIsBlurred(!isBlurred);

let texts = Array.isArray(data.scan.ocr?.text)
? data.scan.ocr.text
@@ -27,6 +34,16 @@ const OcrOverviewCard = ({ data }) => {
return <div className="thumbnail-placeholder" />;
};

// Conditional styling for blurred image (QR codes)
const imageStyle = isBlurred
? {
filter: "blur(4px)",
cursor: "pointer",
}
: {
cursor: "pointer",
};

// Function to create line numbers and corresponding text
const renderTextLines = (texts) => {
let lineNumber = 1; // Initialize line number
@@ -83,19 +100,27 @@ const OcrOverviewCard = ({ data }) => {
</table>
</Col>
<Col span={5} className="thumbnail-container">
{base64Thumbnail ? (
<>
{base64Thumbnail ? (
<div className="thumbnail-wrapper">
<img
src={`data:image/jpeg;base64,${base64Thumbnail}`}
alt="Email Preview"
style={{
width: "auto",
maxHeight: "200px",
overflowY: "auto",
cursor: "pointer",
}}
style={imageStyle}
onClick={showModal}
/>
{isBlurred && (
<div>
<Tooltip title="QR codes can pose a security risk, use with caution">
<Button
onClick={toggleBlur}
danger
className="centered-button"
>
Remove Blur
</Button>
</Tooltip>
</div>
)}
<Modal
open={isModalVisible}
footer={null}
@@ -107,7 +132,7 @@ const OcrOverviewCard = ({ data }) => {
style={{ width: "100%" }}
/>
</Modal>
</>
</div>
) : (
<ThumbnailPlaceholder />
)}
56 changes: 56 additions & 0 deletions ui/src/components/FileComponents/QrOverviewCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useState } from "react";
import { Input, Table, Typography } from "antd";
import "../../styles/IocOverviewCard.css";

const { Text } = Typography;

const QrOverviewCard = ({ data }) => {
const [filter, setFilter] = useState("");

const columns = [
{
title: "Data",
dataIndex: "data",
key: "data",
render: (text) => (
<Text code copyable>
{text}
</Text>
),
},
];

const processQrData = () => {
// Assuming data.scan.qr.data is an array of strings (URLs)
return data.scan.qr.data
.filter((url) => {
const searchTerm = filter.toLowerCase();
return !filter || url.toLowerCase().includes(searchTerm);
})
.map((data, index) => ({
key: index,
data: data,
}));
};

const filteredQrData = processQrData();

return (
<div>
<Input
placeholder="Filter QR Codes"
onChange={(e) => setFilter(e.target.value)}
style={{ width: "100%", marginBottom: 16 }}
/>
<Table
columns={columns}
dataSource={filteredQrData}
pagination={{ pageSize: 10 }}
scroll={{ y: 200 }}
className="qr-table"
/>
</div>
);
};

export default QrOverviewCard;
37 changes: 30 additions & 7 deletions ui/src/components/FileFlow/NodeTypes/EventNode.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { memo } from "react";
import styled, { createGlobalStyle } from "styled-components";
import { CameraOutlined } from "@ant-design/icons";
import { CameraOutlined, QrcodeOutlined } from "@ant-design/icons";
import { Tag, Tooltip } from "antd";
import { Handle, Position } from "reactflow";
import { getIconConfig } from "../../../utils/iconMappingTable";
@@ -55,6 +55,21 @@ function getVirusTotalStatus(virusTotalResponse) {
}

// Styled Components
const QrCodePreviewWrapper = styled.div`
position: absolute;
bottom: 10px;
right: ${({ hasImage }) => (hasImage ? "40px" : "10px")};
width: 24px;
height: 24px;
border-radius: 20%;
background-color: #ff7a45;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0px 4px 6px -1px rgba(0, 0, 0, 0.1);
cursor: pointer;
`;

const PulsatingAnimation = createGlobalStyle`
@keyframes pulsate {
0% {
@@ -97,15 +112,15 @@ const ImageTooltip = styled(Tooltip)`
`;

const PreviewImage = styled.img`
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
`;

const TagWrapper = styled.div`
display: inline-block;
margin-right: 10px;
display: inline-block;
margin-right: 10px;
position: absolute;
top: -15px;
z-index: 10;
@@ -235,6 +250,7 @@ const EventNode = memo(({ data, selected }) => {
const mappingEntry = getIconConfig("strelka", data.nodeMain.toLowerCase());
const IconComponent = mappingEntry?.icon;
const color = mappingEntry?.color || data.color;
const hasImage = Boolean(data.nodeImage);
const virusTotalResponse = data.nodeVirustotal;
data.nodeAlert =
typeof virusTotalResponse === "number" && virusTotalResponse > 5;
@@ -302,6 +318,13 @@ const EventNode = memo(({ data, selected }) => {
<b>{getVirusTotalStatus(data.nodeVirustotal)}</b>
</Tag>
</VirustotalWrapper>
{data.nodeQrData && (
<Tooltip title="QR Code found">
<QrCodePreviewWrapper hasImage={hasImage}>
<QrcodeOutlined style={{ color: "white" }} />
</QrCodePreviewWrapper>
</Tooltip>
)}
{data.nodeImage && (
<ImageTooltip
color="white"
38 changes: 36 additions & 2 deletions ui/src/pages/SubmissionView.js
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ import FileHighlightsOverviewCard from "../components/FileComponents/FileHighlig
import VbOverviewCard from "../components/FileComponents/VbOverviewCard";
import JavascriptOverviewCard from "../components/FileComponents/JavascriptOverviewCard";
import EmailOverviewCard from "../components/FileComponents/EmailOverviewCard";
import QrOverviewCard from "../components/FileComponents/QrOverviewCard";

import { getIconConfig } from "../utils/iconMappingTable";

@@ -513,6 +514,39 @@ const SubmissionsPage = (props) => {
</Collapse.Panel>
</Collapse>
)}
{selectedNodeData && selectedNodeData.scan?.qr?.data && (
<Collapse
defaultActiveKey={[]}
style={{ width: "100%", marginBottom: "10px" }}
>
<Collapse.Panel
header={
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginLeft: "8px" }}>
{" "}
<Text strong>QR Code Data</Text>
<div style={{ fontSize: "smaller", color: "#888" }}>
{selectedNodeData.scan.qr.data.length > 0
? "QR Data Count: " + selectedNodeData.scan.qr.data.length
: "No QR Data"}
</div>
</div>
</div>
</div>
}
key="1"
>
<QrOverviewCard data={selectedNodeData} />
</Collapse.Panel>
</Collapse>
)}
{selectedNodeData && selectedNodeData.scan.email && (
<Collapse
defaultActiveKey={[]}
@@ -698,8 +732,8 @@ const SubmissionsPage = (props) => {
<div>
<Text strong>Indicators of Compromise (IOCs)</Text>
<div style={{ fontSize: "smaller", color: "#888" }}>
{selectedNodeData.iocs[0].ioc} and{" "}
{selectedNodeData.iocs.length - 1} more
{selectedNodeData.iocs[0].ioc} {" "}
{selectedNodeData.iocs.length > 1 && ` and ${selectedNodeData.iocs.length - 1} more`}
</div>
</div>
<div style={{ fontSize: "smaller", color: "#888" }}>
197 changes: 104 additions & 93 deletions ui/src/utils/indexDataUtils.js
Original file line number Diff line number Diff line change
@@ -1,101 +1,112 @@
// Constants for index colors
const COLORS = {
STRELKA: "#9c27b0",
DEFAULT: "#ff0072"
};

/**
* Returns the color theme based on the given index.
*
* @param index - The index for which the color is to be fetched.
*
* @returns A color string.
*/
export const indexColorThemes = (index) => {
switch (index) {
case "strelka":
return COLORS.STRELKA;
default:
return COLORS.DEFAULT;
}
STRELKA: "#9c27b0",
DEFAULT: "#ff0072",
};

/**
* Returns the color theme based on the given index.
*
* @param index - The index for which the color is to be fetched.
*
* @returns A color string.
*/
export const indexColorThemes = (index) => {
switch (index) {
case "strelka":
return COLORS.STRELKA;
default:
return COLORS.DEFAULT;
}
};

/**
* Transforms raw data into a structured nodeData object based on the index.
*
* @param index - The type/index of the data.
* @param data - The raw data to be transformed.
*
* @returns A structured nodeData object.
*/
export const indexDataType = (index, data) => {
let nodeData = {
nodeDatatype: "strelka",
nodeVirustotal: "Not Found",
nodeInsights: 0,
nodeIocs: 0,
nodeImage: "",
nodeDisposition: "",
nodeMain: "",
nodeSub: "",
nodeLabel: "",
nodeYaraList: "",
nodeMetric: "",
nodeParentId: "",
nodeRelationshipId: "",
};

/**
* Transforms raw data into a structured nodeData object based on the index.
*
* @param index - The type/index of the data.
* @param data - The raw data to be transformed.
*
* @returns A structured nodeData object.
*/
export const indexDataType = (index, data) => {
let nodeData = {
nodeDatatype: "strelka",
nodeVirustotal: "Not Found",
nodeInsights: 0,
nodeIocs: 0,
nodeImage: "",
nodeDisposition: "",
nodeMain: "",
nodeSub: "",
nodeLabel: "",
nodeYaraList: "",
nodeMetric: "",
nodeParentId: "",
nodeRelationshipId: "",
};
switch (index) {
case "strelka":
switch (index) {
case "strelka":

// Extracting the base64_thumbnail from _any_ scanner, if present
let base64Thumbnail = '';
if (data["scan"]) {
for (let key in data["scan"]) {
if (data["scan"][key]["base64_thumbnail"]) {
base64Thumbnail = data["scan"][key]["base64_thumbnail"];
break;
}
// Check if QR data exists and add to nodeData
let qrData = "";
if (data.scan?.qr?.data) {
qrData = data.scan.qr.data;
}

// Extracting the base64_thumbnail from _any_ scanner, if present
let base64Thumbnail = "";
if (data["scan"]) {
for (let key in data["scan"]) {
if (data["scan"][key]["base64_thumbnail"]) {
base64Thumbnail = data["scan"][key]["base64_thumbnail"];
break;
}
}
}

Object.assign(nodeData, {
nodeDepth: data["file"]["depth"],
nodeMain: data["file"]["flavors"]["yara"]?.[0] || data["file"]["flavors"]["mime"]?.[0],
nodeSub: `${data["file"]["size"]} Bytes`,
nodeLabel: data["file"]["name"] || "No Filename",
nodeYaraList: data["scan"]?.["yara"]?.["matches"] || "",
nodeMetric: data["scan"]?.["yara"]?.["matches"]?.length || 0,
nodeMetricLabel: "Yara Matches",
nodeParentId: data["file"]["tree"]["parent"],
nodeRelationshipId: data["file"]["tree"]["node"],
nodeVirustotal: data["enrichment"]?.["virustotal"] !== undefined ? data["enrichment"]["virustotal"] : "Not Found",
nodeInsights: data?.insights?.length,
nodeIocs: data?.iocs?.length,
nodeImage: base64Thumbnail,
}
);
break;

default:
break;
}
Object.assign(nodeData, {
nodeDepth: data["file"]["depth"],
nodeMain:
data["file"]["flavors"]["yara"]?.[0] ||
data["file"]["flavors"]["mime"]?.[0],
nodeSub: `${data["file"]["size"]} Bytes`,
nodeLabel: data["file"]["name"] || "No Filename",
nodeYaraList: data["scan"]?.["yara"]?.["matches"] || "",
nodeMetric: data["scan"]?.["yara"]?.["matches"]?.length || 0,
nodeMetricLabel: "Yara Matches",
nodeParentId: data["file"]["tree"]["parent"],
nodeRelationshipId: data["file"]["tree"]["node"],
nodeVirustotal:
data["enrichment"]?.["virustotal"] !== undefined
? data["enrichment"]["virustotal"]
: "Not Found",
nodeInsights: data?.insights?.length,
nodeIocs: data?.iocs?.length,
nodeImage: base64Thumbnail,
nodeQrData: qrData
});
break;

return nodeData;
};

/**
* Transforms raw data into a structured nodeData object based on the index.
*
* @param index - The type/index of the data.
* @param data - The raw data to be transformed.
*
* @returns A structured nodeData object.
*/
export const indexNodeType = (index) => {
switch (index) {
case "strelka":
return "event";
default:
return "event";
}
};
default:
break;
}

return nodeData;
};

/**
* Transforms raw data into a structured nodeData object based on the index.
*
* @param index - The type/index of the data.
* @param data - The raw data to be transformed.
*
* @returns A structured nodeData object.
*/
export const indexNodeType = (index) => {
switch (index) {
case "strelka":
return "event";
default:
return "event";
}
};
1 change: 1 addition & 0 deletions ui/src/utils/layoutUtils.js
Original file line number Diff line number Diff line change
@@ -104,6 +104,7 @@ export function transformElasticSearchDataToElements(results) {
nodeInsights: nodeData.nodeInsights,
nodeIocs: nodeData.nodeIocs,
nodeImage: nodeData.nodeImage,
nodeQrData: nodeData.nodeQrData,
nodeMain: nodeData.nodeMain,
nodeSub: nodeData.nodeSub,
nodeLabel: nodeData.nodeLabel,