+
+
diff --git a/webui/src/components/ProcessCapabilities.vue b/webui/src/components/ProcessCapabilities.vue
index 43b8d767..1cfb27e5 100644
--- a/webui/src/components/ProcessCapabilities.vue
+++ b/webui/src/components/ProcessCapabilities.vue
@@ -150,7 +150,8 @@ const processTree = computed(() => {
})
// build the final tree structure
const rootProcesses = []
- processMap.forEach((processNode, pid) => {
+ processMap.forEach((processNode) => {
+ console.log(processNode)
processNode.data.uniqueRules = Array.from(processNode.data.uniqueRules.values())
const parentProcess = processMap.get(processNode.data.ppid)
if (parentProcess) {
diff --git a/webui/src/components/RuleMatchesTable.vue b/webui/src/components/RuleMatchesTable.vue
index 2aa6b1a1..994b7e1b 100644
--- a/webui/src/components/RuleMatchesTable.vue
+++ b/webui/src/components/RuleMatchesTable.vue
@@ -301,25 +301,21 @@ onMounted(() => {
})
/**
- * Formats an MBC (Malware Behavior Catalog) object into a string representation.
+ * Creates an MBC (Malware Behavior Catalog) URL from an MBC object.
*
* @param {Object} mbc - The MBC object to format.
- * @returns {string} A string representation of the MBC object.
- *
- * e.g. "Anti-Behavioral Analysis::Virtual Machine Detection::Human User Check [B0009.012]"
+ * @returns {string} The MBC URL.
*/
-const formatMBC = (mbc) => {
- return `${mbc.parts.join('::')} [${mbc.id}]`
-}
-
function createMBCHref(mbc) {
let baseUrl;
// Determine the base URL based on the id
if (mbc.id.startsWith('B')) {
+ // Behavior
baseUrl = 'https://github.com/MBCProject/mbc-markdown/blob/main';
} else if (mbc.id.startsWith('C')) {
+ // Micro-Behavior
baseUrl = 'https://github.com/MBCProject/mbc-markdown/blob/main/micro-behaviors';
} else {
return null
@@ -345,7 +341,7 @@ function createMBCHref(mbc) {
const idParts = attack.id.split('.');
if (idParts.length === 1) {
- // It's a main technique
+ // It's a technique
return `${baseUrl}${idParts[0]}`;
} else if (idParts.length === 2) {
// It's a sub-technique
@@ -366,7 +362,7 @@ function createMBCHref(mbc) {
/* Make all matches nodes (i.e. not rule names) slightly smaller */
.p-treetable-tbody > tr:not(:is([aria-level='1'])) > td {
- font-size: 0.9rem;
+ font-size: 0.95rem;
}
/* Optional: Add a subtle background to root-level rows for better distinction */
diff --git a/webui/src/components/columns/RuleColumn.vue b/webui/src/components/columns/RuleColumn.vue
index 13f2bf7b..837c527c 100644
--- a/webui/src/components/columns/RuleColumn.vue
+++ b/webui/src/components/columns/RuleColumn.vue
@@ -16,7 +16,7 @@
- {{ node.data.typeValue }}: {{ node.data.name }}
-
+
= {{ node.data.description }}
diff --git a/webui/src/utils/rdocParser.js b/webui/src/utils/rdocParser.js
index b40a0e3c..6415d519 100644
--- a/webui/src/utils/rdocParser.js
+++ b/webui/src/utils/rdocParser.js
@@ -71,64 +71,59 @@ export function parseRules(rules, flavor) {
* Parses rules data for the CapasByFunction component
* @param {Object} data - The full JSON data object containing analysis results
* @param {boolean} showLibraryRules - Whether to include library rules in the output
- * @returns {Array} - Parsed tree data for the CapasByFunction component
+ * @returns {Array} - Parsed data for the CapasByFunction DataTable component
*/
export function parseFunctionCapabilities(data, showLibraryRules) {
- const result = []
+ const result = [];
+ let id = 0;
// Iterate through each function in the metadata
for (const functionInfo of data.meta.analysis.layout.functions) {
// Convert function address to uppercase hexadecimal string
- const functionAddress = functionInfo.address.value.toString(16).toUpperCase()
- const matchingRules = []
+ const functionAddress = functionInfo.address.value.toString(16).toUpperCase();
+ const matchingRules = [];
// Iterate through all rules in the data
for (const ruleId in data.rules) {
- const rule = data.rules[ruleId]
+ const rule = data.rules[ruleId];
// Skip library rules if showLibraryRules is false
if (!showLibraryRules && rule.meta.lib) {
- continue
+ continue;
}
// Find matches for this rule within the current function
const matches = rule.matches.filter((match) =>
// Check if any of the function's basic blocks match the rule
functionInfo.matched_basic_blocks.some((block) => block.address.value === match[0].value)
- )
+ );
// If there are matches, add this rule to the matchingRules array
if (matches.length > 0) {
matchingRules.push({
- key: `${functionAddress}-${matchingRules.length}`, // Unique key for each rule
- data: {
- funcaddr: `${rule.meta.name}`,
- lib: rule.meta.lib,
- matchcount: null,
- namespace: rule.meta.namespace,
- source: rule.source
- }
- })
+ ruleName: rule.meta.name,
+ lib: rule.meta.lib,
+ namespace: rule.meta.namespace,
+ source: rule.source
+ });
}
}
// If there are matching rules for this function, add it to the result
if (matchingRules.length > 0) {
- result.push({
- key: functionAddress, // Use function address as key
- data: {
- funcaddr: `function: 0x${functionAddress}`,
- lib: false, // Functions are not library rules
- matchcount: matchingRules.length, // Number of matching rules for this function
- namespace: null, // Functions don't have a namespace
- source: null // Functions don't have source code in this context
- },
- children: matchingRules // Add matching rules as children
- })
+ // Add each matching rule as a separate row
+ matchingRules.forEach(rule => {
+ result.push({
+ id: id++,
+ funcaddr: `0x${functionAddress}`,
+ matchcount: matchingRules.length,
+ ...rule
+ });
+ });
}
}
- return result
+ return result;
}
/**