Offline HTML analysis reports (#1158)

This commit is contained in:
Paweł Srokosz
2026-02-18 14:08:20 +01:00
committed by GitHub
parent 697d532297
commit 699e8a0c6f
15 changed files with 686 additions and 160 deletions
@@ -3,6 +3,7 @@ from drakrun.lib.paths import DUMPS_DIR, DUMPS_ZIP, IPT_DIR, IPT_ZIP
from .build_process_tree import build_process_tree
from .capa_plugin.capa_processor import capa_analysis
from .compress_ipt import compress_ipt
from .generate_html_report import generate_html_report
from .generate_report import generate_report
from .generate_wireshark_key_file import generate_wireshark_key_file
from .get_http_info import get_http_info
@@ -85,6 +86,11 @@ POSTPROCESS_PLUGINS = [
requires=[],
generates=[],
),
PostprocessPlugin(
function=generate_html_report,
requires=["report.json"],
generates=["report.html"],
),
PostprocessPlugin(
function=index_logs, requires=["process_tree.json"], generates=["log_index"]
),
@@ -0,0 +1,50 @@
import base64
import json
import logging
from drakrun.lib.paths import PACKAGE_DIR
from .plugin_base import PostprocessContext
logger = logging.getLogger(__name__)
HTML_TEMPLATE = PACKAGE_DIR / "web/frontend/dist/embedded/index.html"
OFFLINE_FILES_PLACEHOLDER = "window.OFFLINE_FILES = {};"
def png_bytes_to_data_uri(png_bytes: bytes):
encoded = base64.b64encode(png_bytes).decode("ascii")
return f"data:image/png;base64,{encoded}"
def generate_html_report(context: PostprocessContext) -> None:
offline_files = {
"metadata.json": {
**context.metadata.store_to_dict(),
**context.extra_metadata,
}
}
report_file = context.analysis_dir / "report.json"
with report_file.open("r") as f:
offline_files["report.json"] = json.load(f)
process_tree_file = context.analysis_dir / "process_tree.json"
if process_tree_file.exists():
with process_tree_file.open("r") as f:
offline_files["process_tree.json"] = json.load(f)
offline_files["screenshots"] = []
screenshots_count = offline_files["metadata.json"].get("screenshots", 0)
for which in range(screenshots_count):
with (context.analysis_dir / f"screenshots/screenshot_{which+1}.png").open(
"rb"
) as f:
screenshot_data_uri = png_bytes_to_data_uri(f.read())
offline_files["screenshots"].append(screenshot_data_uri)
html_report = HTML_TEMPLATE.read_text().replace(
OFFLINE_FILES_PLACEHOLDER, f"window.OFFLINE_FILES={json.dumps(offline_files)};"
)
(context.analysis_dir / "report.html").write_text(html_report)
context.update_metadata({"html_report": True})
+12
View File
@@ -389,3 +389,15 @@ def screenshot(path: ScreenshotRequestPath):
mimetype="image/png",
s3_config=config.s3,
)
@api.get("/html/<task_uid>")
def get_html_report(path: AnalysisRequestPath):
task_uid = path.task_uid
return send_analysis_file(
task_uid,
"report.html",
mimetype="text/html",
s3_config=config.s3,
download_name=f"report-{task_uid}.html",
)
+15
View File
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DRAKVUF Sandbox</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="../src/html_report_main.jsx"></script>
<script>
window.OFFLINE_FILES = {};
</script>
</body>
</html>
@@ -0,0 +1,45 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { viteSingleFile } from "vite-plugin-singlefile";
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
import { gzipSync } from "node:zlib";
function viteGzipBundle() {
return {
name: "vite:gzip-bundle-plugin",
apply: "build",
enforce: "post",
generateBundle(_, bundle) {
for (const [fileName, chunk] of Object.entries(bundle)) {
if (chunk.type === "chunk" && fileName.endsWith(".js")) {
console.log("[gzip] ", fileName);
const originalCode = chunk.code;
const gzipped = gzipSync(
Buffer.from(originalCode, "utf-8"),
);
const base64 = gzipped.toString("base64");
const loader = `await import(URL.createObjectURL(new Blob([await new Response((await fetch('data:;base64,${base64}')).body.pipeThrough(new DecompressionStream('gzip'))).text()],{type:'application/javascript'})))`;
chunk.code = loader;
}
}
},
};
}
// https://vite.dev/config/
export default defineConfig({
root: "./embedded",
plugins: [
react(),
cssInjectedByJsPlugin(),
viteGzipBundle(),
viteSingleFile(),
],
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
},
build: {
outDir: "../dist/embedded",
emptyOutDir: true,
},
});
+305 -92
View File
@@ -36,7 +36,9 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"vite": "^6.4.1"
"vite": "^6.4.1",
"vite-plugin-css-injected-by-js": "^3.5.2",
"vite-plugin-singlefile": "^2.3.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -79,6 +81,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
"dev": true,
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2",
@@ -1010,6 +1013,7 @@
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
"peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
@@ -1195,260 +1199,350 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
"integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
"integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz",
"integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
"integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz",
"integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
"integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz",
"integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
"integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz",
"integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
"integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz",
"integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
"integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz",
"integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
"integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz",
"integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
"integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz",
"integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
"integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz",
"integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
"integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz",
"integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
"integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz",
"integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
"integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
"integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
"integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz",
"integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
"integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz",
"integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
"integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz",
"integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
"integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz",
"integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
"integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz",
"integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
"integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz",
"integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
"integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
"integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
"integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz",
"integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
"integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
"integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz",
"integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
"integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -1496,10 +1590,11 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
@@ -1516,6 +1611,7 @@
"version": "19.1.2",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
"integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -1561,6 +1657,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"dev": true,
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1679,6 +1776,19 @@
"concat-map": "0.0.1"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browserslist": {
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
@@ -1698,6 +1808,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73",
@@ -2035,6 +2146,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz",
"integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==",
"dev": true,
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -2248,6 +2360,19 @@
"node": ">=16.0.0"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
@@ -2574,6 +2699,16 @@
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -2755,6 +2890,33 @@
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -2958,6 +3120,7 @@
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -3049,6 +3212,7 @@
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3057,6 +3221,7 @@
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@@ -3235,12 +3400,14 @@
}
},
"node_modules/rollup": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
"integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
"integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.7"
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -3250,26 +3417,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.40.0",
"@rollup/rollup-android-arm64": "4.40.0",
"@rollup/rollup-darwin-arm64": "4.40.0",
"@rollup/rollup-darwin-x64": "4.40.0",
"@rollup/rollup-freebsd-arm64": "4.40.0",
"@rollup/rollup-freebsd-x64": "4.40.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.40.0",
"@rollup/rollup-linux-arm-musleabihf": "4.40.0",
"@rollup/rollup-linux-arm64-gnu": "4.40.0",
"@rollup/rollup-linux-arm64-musl": "4.40.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.40.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.40.0",
"@rollup/rollup-linux-riscv64-gnu": "4.40.0",
"@rollup/rollup-linux-riscv64-musl": "4.40.0",
"@rollup/rollup-linux-s390x-gnu": "4.40.0",
"@rollup/rollup-linux-x64-gnu": "4.40.0",
"@rollup/rollup-linux-x64-musl": "4.40.0",
"@rollup/rollup-win32-arm64-msvc": "4.40.0",
"@rollup/rollup-win32-ia32-msvc": "4.40.0",
"@rollup/rollup-win32-x64-msvc": "4.40.0",
"@rollup/rollup-android-arm-eabi": "4.57.1",
"@rollup/rollup-android-arm64": "4.57.1",
"@rollup/rollup-darwin-arm64": "4.57.1",
"@rollup/rollup-darwin-x64": "4.57.1",
"@rollup/rollup-freebsd-arm64": "4.57.1",
"@rollup/rollup-freebsd-x64": "4.57.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
"@rollup/rollup-linux-arm-musleabihf": "4.57.1",
"@rollup/rollup-linux-arm64-gnu": "4.57.1",
"@rollup/rollup-linux-arm64-musl": "4.57.1",
"@rollup/rollup-linux-loong64-gnu": "4.57.1",
"@rollup/rollup-linux-loong64-musl": "4.57.1",
"@rollup/rollup-linux-ppc64-gnu": "4.57.1",
"@rollup/rollup-linux-ppc64-musl": "4.57.1",
"@rollup/rollup-linux-riscv64-gnu": "4.57.1",
"@rollup/rollup-linux-riscv64-musl": "4.57.1",
"@rollup/rollup-linux-s390x-gnu": "4.57.1",
"@rollup/rollup-linux-x64-gnu": "4.57.1",
"@rollup/rollup-linux-x64-musl": "4.57.1",
"@rollup/rollup-openbsd-x64": "4.57.1",
"@rollup/rollup-openharmony-arm64": "4.57.1",
"@rollup/rollup-win32-arm64-msvc": "4.57.1",
"@rollup/rollup-win32-ia32-msvc": "4.57.1",
"@rollup/rollup-win32-x64-gnu": "4.57.1",
"@rollup/rollup-win32-x64-msvc": "4.57.1",
"fsevents": "~2.3.2"
}
},
@@ -3426,6 +3598,19 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -3530,6 +3715,7 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -3599,6 +3785,33 @@
}
}
},
"node_modules/vite-plugin-css-injected-by-js": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.2.tgz",
"integrity": "sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"vite": ">2.0.0-0"
}
},
"node_modules/vite-plugin-singlefile": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.0.tgz",
"integrity": "sha512-DAcHzYypM0CasNLSz/WG0VdKOCxGHErfrjOoyIPiNxTPTGmO6rRD/te93n1YL/s+miXq66ipF1brMBikf99c6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"micromatch": "^4.0.8"
},
"engines": {
"node": ">18.0.0"
},
"peerDependencies": {
"rollup": "^4.44.1",
"vite": "^5.4.11 || ^6.0.0 || ^7.0.0"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+4 -2
View File
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build": "vite build && vite build --config embedded_report.vite.config.js",
"lint": "eslint .",
"preview": "vite preview"
},
@@ -38,7 +38,9 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"vite": "^6.4.1"
"vite": "^6.4.1",
"vite-plugin-css-injected-by-js": "^3.5.2",
"vite-plugin-singlefile": "^2.3.0"
},
"prettier": {
"tabWidth": 4
+87 -53
View File
@@ -1,11 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import {
getAnalysisSummary,
getLog,
getLogList,
getProcessInfo,
getProcessLog,
} from "./api.js";
import { getLog, getLogList, getProcessLog } from "./api.js";
import { TabSwitcher, Tab, useTabContext } from "./TabSwitcher.jsx";
import { getLogLoader, LogViewer } from "./LogViewer.jsx";
import { AnalysisMetadataTable } from "./AnalysisMetadataTable.jsx";
@@ -19,6 +13,7 @@ import axios, { CanceledError } from "axios";
import { AnalysisSummary } from "./AnalysisSummary.jsx";
import { ProcessBadge } from "./ProcessBadge.jsx";
import { AnalysisFilesViewer } from "./AnalysisFiles.jsx";
import { useAnalysisReport } from "./AnalysisReportContext.jsx";
export function AnalysisLogViewerTab({ analysisId }) {
const logType = useTabContext()?.activeTab;
@@ -136,6 +131,7 @@ function ProcessLogViewer({ analysisId, selectedProcess }) {
const [activeTab, setActiveTab] = useState();
const [processInfo, setProcessInfo] = useState();
const [error, setError] = useState();
const { getProcessInfo, isOffline } = useAnalysisReport();
const loadProcessInfo = useCallback(
async (processSeqId) => {
try {
@@ -194,11 +190,15 @@ function ProcessLogViewer({ analysisId, selectedProcess }) {
selectedProcess={selectedProcess}
processInfo={processInfo}
/>
<ProcessLogViewerTab
analysisId={analysisId}
selectedProcess={selectedProcess}
processInfo={processInfo}
/>
{!isOffline ? (
<ProcessLogViewerTab
analysisId={analysisId}
selectedProcess={selectedProcess}
processInfo={processInfo}
/>
) : (
[]
)}
</TabSwitcher>
</div>
</div>
@@ -220,6 +220,7 @@ function AnalysisReportTabs({
setSelectedProcess,
activeReportTab,
setActiveReportTab,
isOffline,
}) {
const getHeader = useCallback(
(tab) => {
@@ -256,9 +257,13 @@ function AnalysisReportTabs({
setSelectedProcess={setSelectedProcess}
/>
</PaddedTab>
<PaddedTab tab="General logs">
<AnalysisLogViewer analysisId={analysis.id} />
</PaddedTab>
{!isOffline ? (
<PaddedTab tab="General logs">
<AnalysisLogViewer analysisId={analysis.id} />
</PaddedTab>
) : (
[]
)}
<PaddedTab tab="Process info">
<ProcessLogViewer
analysisId={analysis.id}
@@ -270,19 +275,28 @@ function AnalysisReportTabs({
<AnalysisScreenshotViewer analysis={analysis} />
</PaddedTab>
) : null}
<PaddedTab tab="Analysis files">
<AnalysisFilesViewer analysisId={analysis.id} />
</PaddedTab>
{!isOffline ? (
<PaddedTab tab="Analysis files">
<AnalysisFilesViewer analysisId={analysis.id} />
</PaddedTab>
) : (
[]
)}
</TabSwitcher>
</div>
</div>
);
}
export function AnalysisReport({ analysis }) {
export function AnalysisReport() {
const [selectedProcess, setSelectedProcess] = useState();
const [activeReportTab, setActiveReportTab] = useState();
const [analysisSummary, setAnalysisSummary] = useState();
const {
analysisInfo: analysis,
getAnalysisSummary,
isOffline,
} = useAnalysisReport();
const plugins = analysis.options?.plugins;
const baseUrl = axios.defaults.baseURL;
const analysisId = analysis.id;
@@ -298,7 +312,7 @@ export function AnalysisReport({ analysis }) {
console.error(error);
}
});
}, [analysisId]);
}, [analysisId, getAnalysisSummary]);
useEffect(() => {
fetchSummary();
@@ -324,47 +338,66 @@ export function AnalysisReport({ analysis }) {
</div>
<div className="col-6">
<AnalysisMetadataTable analysis={analysis} />
<div className="card">
<div className="card-body">
<a href={`${baseUrl}/pcap_file/${analysis.id}`}>
<button className="btn btn-primary m-1">
<FontAwesomeIcon
icon={faDownload}
className="me-2"
/>
Download PCAP
</button>
</a>
{Array.isArray(plugins) &&
plugins.includes("tlsmon") ? (
<a href={`${baseUrl}/pcap_keys/${analysis.id}`}>
{!isOffline ? (
<div className="card">
<div className="card-body">
<a href={`${baseUrl}/pcap_file/${analysis.id}`}>
<button className="btn btn-primary m-1">
<FontAwesomeIcon
icon={faDownload}
className="me-2"
/>
TLS keys
Download PCAP
</button>
</a>
) : (
[]
)}
{Array.isArray(plugins) &&
plugins.includes("memdump") ? (
<a href={`${baseUrl}/dumps/${analysis.id}`}>
<button className="btn btn-primary m-1">
<FontAwesomeIcon
icon={faDownload}
className="me-2"
/>
Memory dumps
</button>
</a>
) : (
[]
)}
{Array.isArray(plugins) &&
plugins.includes("tlsmon") ? (
<a
href={`${baseUrl}/pcap_keys/${analysis.id}`}
>
<button className="btn btn-primary m-1">
<FontAwesomeIcon
icon={faDownload}
className="me-2"
/>
TLS keys
</button>
</a>
) : (
[]
)}
{Array.isArray(plugins) &&
plugins.includes("memdump") ? (
<a href={`${baseUrl}/dumps/${analysis.id}`}>
<button className="btn btn-primary m-1">
<FontAwesomeIcon
icon={faDownload}
className="me-2"
/>
Memory dumps
</button>
</a>
) : (
[]
)}
{analysis["html_report"] ? (
<a href={`${baseUrl}/html/${analysis.id}`}>
<button className="btn btn-primary m-1">
<FontAwesomeIcon
icon={faDownload}
className="me-2"
/>
HTML report
</button>
</a>
) : (
[]
)}
</div>
</div>
</div>
) : (
[]
)}
</div>
</div>
<div className="row py-4">
@@ -376,6 +409,7 @@ export function AnalysisReport({ analysis }) {
setSelectedProcess={onSelectProcess}
activeReportTab={activeReportTab}
setActiveReportTab={setActiveReportTab}
isOffline={isOffline}
/>
</div>
</div>
@@ -0,0 +1,91 @@
import React, { useContext, useMemo } from "react";
import {
getAnalysisSummary,
getProcessInfo,
getAnalysisProcessTree,
} from "./api.js";
const AnalysisReportContext = React.createContext({});
let BASE_URL = "";
if (import.meta.env.VITE_API_SERVER) {
BASE_URL = import.meta.env.VITE_API_SERVER;
} else {
BASE_URL = "/api";
}
export function useAnalysisReport() {
return useContext(AnalysisReportContext);
}
export function OnlineAnalysisReportProvider({ analysisInfo, children }) {
const contextValue = useMemo(
() => ({
analysisInfo,
getAnalysisSummary,
getProcessInfo,
getAnalysisProcessTree,
getScreenshotURL: (analysis_id, screenshot_idx) =>
`${BASE_URL}/screenshot/${analysis_id}/${screenshot_idx}`,
isOffline: false,
}),
[analysisInfo],
);
return (
<AnalysisReportContext.Provider value={contextValue}>
{children}
</AnalysisReportContext.Provider>
);
}
export function OfflineAnalysisReportProvider({ reportData, children }) {
const contextValue = useMemo(
() => ({
analysisInfo: reportData["metadata.json"],
getAnalysisSummary: () =>
new Promise((resolve) => {
resolve(reportData["report.json"]);
}),
getProcessInfo: ({ processSeqId }) =>
new Promise((resolve) => {
// Conversion between schemas if /api/report and /api/process_info
const processes = reportData["report.json"].processes;
const processInfo = processes[processSeqId];
const ppid =
processInfo["parent_seqid"] !== null
? processes[processInfo["parent_seqid"]].pid
: null;
resolve({
logs: {},
process: {
...processInfo,
// evtid: We don't have it in report.json and don't need it
// as we don't have offline logs for now. If needed, change
// this code to fetch information from process_tree.json
// instead of report.json (process_tree needs tree traversal)
evtid_from: 0,
evtid_to: 0,
ppid: ppid,
procname: processInfo["name"],
ts_from: processInfo["started_at"],
ts_to: processInfo["exited_at"],
},
});
}),
getAnalysisProcessTree: () =>
new Promise((resolve) => {
resolve(reportData["process_tree.json"]);
}),
getScreenshotURL: (analysis_id, screenshot_idx) =>
reportData["screenshots"] &&
reportData["screenshots"][screenshot_idx - 1],
isOffline: true,
}),
[reportData],
);
return (
<AnalysisReportContext.Provider value={contextValue}>
{children}
</AnalysisReportContext.Provider>
);
}
@@ -1,14 +1,9 @@
import Zoom from "react-medium-image-zoom";
import "react-medium-image-zoom/dist/styles.css";
let BASE_URL = "";
if (import.meta.env.VITE_API_SERVER) {
BASE_URL = import.meta.env.VITE_API_SERVER;
} else {
BASE_URL = "/api";
}
import { useAnalysisReport } from "./AnalysisReportContext.jsx";
export function AnalysisScreenshotViewer({ analysis }) {
const { getScreenshotURL } = useAnalysisReport();
return (
<div className="container-fluid">
<div className="row">
@@ -18,7 +13,7 @@ export function AnalysisScreenshotViewer({ analysis }) {
<Zoom>
<img
alt={`screenshot-${idx + 1}`}
src={`${BASE_URL}/screenshot/${analysis.id}/${idx + 1}`}
src={getScreenshotURL(analysis.id, idx + 1)}
className="img-thumbnail"
style={{ cursor: "pointer" }}
/>
+6 -1
View File
@@ -8,6 +8,7 @@ import {
AnalysisPendingStatusBox,
} from "./AnalysisPendingView.jsx";
import { AnalysisReport } from "./AnalysisReport.jsx";
import { OnlineAnalysisReportProvider } from "./AnalysisReportContext.jsx";
function AnalysisViewComponent({ analysisId }) {
const checkInterval = useRef(null);
@@ -76,7 +77,11 @@ function AnalysisViewComponent({ analysisId }) {
if (isStatusPending(analysisInfo?.status)) {
return <AnalysisPendingView analysis={analysisInfo} />;
}
return <AnalysisReport analysis={analysisInfo} />;
return (
<OnlineAnalysisReportProvider analysisInfo={analysisInfo}>
<AnalysisReport />
</OnlineAnalysisReportProvider>
);
}
export default function AnalysisView() {
+2 -2
View File
@@ -10,7 +10,7 @@ import AnalysisView from "./AnalysisView.jsx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFolder, faUpload, faGear } from "@fortawesome/free-solid-svg-icons";
function AppHeader() {
export function AppHeader() {
return (
<nav className="sb-topnav navbar navbar-expand navbar-dark bg-dark">
<Link className="navbar-brand ps-3" to="/">
@@ -65,7 +65,7 @@ function AppSidenav() {
);
}
function AppFooter() {
export function AppFooter() {
return (
<footer className="py-4 bg-light mt-auto">
<div className="container-fluid px-4">
+2 -1
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { getAnalysisProcessTree } from "./api.js";
import { ProcessTree } from "./ProcessTree.jsx";
import { useAnalysisReport } from "./AnalysisReportContext.jsx";
function isProcessInteresting(process) {
return process.procname.endsWith("explorer.exe");
@@ -51,6 +51,7 @@ export function ProcessTreeView({
const [uncollapsed, setUncollapsed] = useState(new Set());
const [processTree, setProcessTree] = useState();
const [error, setError] = useState();
const { getAnalysisProcessTree } = useAnalysisReport();
useEffect(() => {
getAnalysisProcessTree({ analysisId })
@@ -0,0 +1,53 @@
import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "bootstrap";
import "bootstrap/dist/css/bootstrap.css";
import "./App.css";
import "startbootstrap-sb-admin/dist/css/styles.css";
import { HashRouter } from "react-router-dom";
import { AppHeader, AppFooter } from "./App";
import { OfflineAnalysisReportProvider } from "./AnalysisReportContext.jsx";
import { AnalysisReport } from "./AnalysisReport.jsx";
function EmbeddedReport() {
return (
<div className="container-fluid px-4">
<h1 className="m-4 h4">Analysis report</h1>
{window.OFFLINE_FILES ? (
<OfflineAnalysisReportProvider
reportData={window.OFFLINE_FILES}
>
<AnalysisReport />
</OfflineAnalysisReportProvider>
) : (
"There are no analysis data embedded in the file"
)}
</div>
);
}
function EmbeddedReportApp() {
return (
<>
<AppHeader />
<div id="layoutSidenav">
<div id="layoutSidenav_content">
<main>
<EmbeddedReport />
</main>
<AppFooter />
</div>
</div>
</>
);
}
createRoot(document.getElementById("root")).render(
<StrictMode>
<HashRouter>
<EmbeddedReportApp />
</HashRouter>
</StrictMode>,
);
+5 -1
View File
@@ -112,7 +112,7 @@ def get_s3_prefix(analysis_id):
return "/".join([*analysis_id[0:4], analysis_id])
def read_analysis_json(analysis_id: str, path: str, s3_config: S3StorageConfigSection):
def read_analysis_file(analysis_id: str, path: str, s3_config: S3StorageConfigSection):
if not is_s3_enabled(s3_config):
base_path = ANALYSES_DIR / analysis_id
path_to_file = check_path(base_path / path, base_path)
@@ -131,7 +131,11 @@ def read_analysis_json(analysis_id: str, path: str, s3_config: S3StorageConfigSe
raise FileNotFoundError from e
else:
raise
return data
def read_analysis_json(analysis_id: str, path: str, s3_config: S3StorageConfigSection):
data = read_analysis_file(analysis_id, path, s3_config)
parsed_data = json.loads(data)
return parsed_data