From 8fd90883b4444fdbdbc3aadab32f5300a3d42364 Mon Sep 17 00:00:00 2001 From: Soufiane Fariss Date: Tue, 20 Aug 2024 23:27:53 +0200 Subject: [PATCH] web: refactor and add support for laoding remote .gz using rdoc query param --- web/explorer/src/components/UploadOptions.vue | 2 +- web/explorer/src/composables/useRdocLoader.js | 104 +++++++++--------- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/web/explorer/src/components/UploadOptions.vue b/web/explorer/src/components/UploadOptions.vue index fe33579f..15555028 100644 --- a/web/explorer/src/components/UploadOptions.vue +++ b/web/explorer/src/components/UploadOptions.vue @@ -80,7 +80,7 @@ const isBundle = import.meta.env.MODE === "bundle"; defineEmits(["load-from-local", "load-from-url"]); const dynamicURL = - "https://raw.githubusercontent.com/mandiant/capa-testfiles/master/rd/0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json"; + "https://raw.githubusercontent.com/mandiant/capa-testfiles/master/rd/0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json.gz"; const staticURL = "https://raw.githubusercontent.com/mandiant/capa-testfiles/master/rd/al-khaser_x64.exe_.json"; diff --git a/web/explorer/src/composables/useRdocLoader.js b/web/explorer/src/composables/useRdocLoader.js index 37149f55..c421d3a7 100644 --- a/web/explorer/src/composables/useRdocLoader.js +++ b/web/explorer/src/composables/useRdocLoader.js @@ -3,35 +3,66 @@ import { isGzipped, decompressGzip, readFileAsText } from "@/utils/fileUtils"; export function useRdocLoader() { const toast = useToast(); - const MIN_SUPPORTED_VERSION = "6.1.0"; + const MIN_SUPPORTED_VERSION = "7.0.0"; /** - * Checks if the loaded rdoc version is supported - * @param {Object} rdoc - The loaded JSON rdoc data - * @returns {boolean} - True if version is supported, false otherwise + * Displays a toast notification. + * @param {string} severity - The severity level of the notification + * @param {string} summary - The title of the notification. + * @param {string} detail - The detailed message of the notification. + * @returns {void} + */ + const showToast = (severity, summary, detail) => { + toast.add({ severity, summary, detail, life: 3000, group: "bc" }); // bc: bottom-center + }; + + /** + * Checks if the version of the loaded data is supported. + * @param {Object} rdoc - The loaded JSON data containing version information. + * @returns {boolean} True if the version is supported, false otherwise. */ const checkVersion = (rdoc) => { const version = rdoc.meta.version; if (version < MIN_SUPPORTED_VERSION) { - console.error( + showToast( + "error", + "Unsupported Version", `Version ${version} is not supported. Please use version ${MIN_SUPPORTED_VERSION} or higher.` ); - toast.add({ - severity: "error", - summary: "Unsupported Version", - detail: `Version ${version} is not supported. Please use version ${MIN_SUPPORTED_VERSION} or higher.`, - life: 5000, - group: "bc" // bottom-center - }); return false; } return true; }; /** - * Loads JSON rdoc data from various sources - * @param {File|string|Object} source - File object, URL string, or JSON object - * @returns {Promise} + * Processes the content of a file or blob. + * @param {File|Blob} blob - The file or blob to process. + * @returns {Promise} A promise that resolves to the parsed JSON data. + * @throws {Error} If the content cannot be parsed as JSON. + */ + const processBlob = async (blob) => { + const content = (await isGzipped(blob)) ? await decompressGzip(blob) : await readFileAsText(blob); + return JSON.parse(content); + }; + + /** + * Fetches data from a URL. + * @param {string} url - The URL to fetch data from. + * @returns {Promise} A promise that resolves to the fetched data as a Blob. + * @throws {Error} If the fetch request fails. + */ + const fetchFromUrl = async (url) => { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.blob(); + }; + + /** + * Loads and processes RDOC data from various sources. + * @param {string|File|Object} source - The source of the RDOC data. Can be a URL string, a File object, or a JSON object. + * @returns {Promise} A promise that resolves to the processed RDOC data, or null if processing fails. */ const loadRdoc = async (source) => { try { @@ -39,54 +70,25 @@ export function useRdocLoader() { if (typeof source === "string") { // Load from URL - const response = await fetch(source); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - data = await response.json(); + const blob = await fetchFromUrl(source); + data = await processBlob(blob); } else if (source instanceof File) { - data = await processFile(source); - } else if (typeof source === "object") { - // Direct JSON object (Preview options) - data = source; + // Load from local + data = await processBlob(source); } else { throw new Error("Invalid source type"); } if (checkVersion(data)) { - toast.add({ - severity: "success", - summary: "Success", - detail: "JSON data loaded successfully", - life: 3000, - group: "bc" // bottom-center - }); + showToast("success", "Success", "JSON data loaded successfully"); return data; } } catch (error) { console.error("Error loading JSON:", error); - toast.add({ - severity: "error", - summary: "Failed to process the file", - detail: error, - life: 3000, - group: "bc" // bottom-center - }); + showToast("error", "Failed to process the file", error.message); } return null; }; - const processFile = async (blob) => { - let fileContent; - if (await isGzipped(blob)) { - fileContent = await decompressGzip(blob); - } else { - fileContent = await readFileAsText(blob); - } - return JSON.parse(fileContent); - }; - - return { - loadRdoc - }; + return { loadRdoc }; }