make rule names and matches click event expand the node

This commit is contained in:
Soufiane Fariss
2024-07-28 19:56:33 +02:00
parent e3f695b947
commit 029259b8ed
3 changed files with 40 additions and 56 deletions

View File

@@ -1,18 +1,20 @@
<template>
<div class="card">
<TreeTable
:value="filteredTreeData"
:value="filteredTreeData"
v-model:expandedKeys="expandedKeys"
size="small"
:filters="filters"
:filterMode="filterMode.value"
:filterMode="filterMode.value"
sortField="namespace"
:sortOrder="-1"
removableSort
:rowHover="true"
:indentation="1.3"
v-model:selectionKeys="selectedNodeKeys"
@node-expand="onNodeExpand"
selectionMode="single"
@nodeExpand="onNodeSelect"
@nodeSelect="onNodeSelect"
:metaKeySelection="false"
>
<template #header>
<div
@@ -178,31 +180,6 @@ const filterMode = ref({ value: 'lenient' })
const sourceDialogVisible = ref(false)
const currentSource = ref('')
const expandedKeys = ref({})
const selectedNodeKeys = ref([])
// Function to handle node expansion
// If one match is is expanded,
// it will collapse all the others
const onNodeExpand = (event) => {
const expandedNodeKey = event.key
const keyParts = expandedNodeKey.split('-')
// Check if the expanded node is a match node (key format: n-m)
if (keyParts.length === 2) {
const parentKey = keyParts[0]
// Collapse all sibling match nodes
Object.keys(expandedKeys.value).forEach((key) => {
if (
key.startsWith(parentKey + '-') &&
key.split('-').length == 2 &&
key !== expandedNodeKey
) {
expandedKeys.value[key] = false
}
})
}
}
// Function to expand all children of a node
const expandAllChildren = (node) => {
@@ -214,11 +191,32 @@ const expandAllChildren = (node) => {
}
}
/*
* Expand node on click
*/
const onNodeSelect = (node) => {
const nodeKey = node.key;
const nodeType = node.data.type;
if (nodeType === 'rule') {
// For rule nodes, clear existing expanded keys and set the clicked rule as expanded
// expand the first (child) match by default
expandedKeys.value = { [nodeKey]: true, [`${nodeKey}-0`]: true };
} else if (nodeType === 'match location') {
// For match location nodes, we need to keep the parent expanded
// and toggle the clicked node while collapsing siblings
const [parentKey, _] = nodeKey.split('-');
expandedKeys.value = { [parentKey]: true, [`${nodeKey}`]: true};
} else {
return
}
};
// All available columns
const togglableColumns = ref([
{ field: 'address', header: 'Address' },
{ field: 'tactic', header: 'ATT&CK Tactic' },
{ field: 'namespace', header: 'Namespace' },
{ field: 'tactic', header: 'ATT&CK Tactic' },
{ field: 'mbc', header: 'Malware Behaviour Catalogue' },
{ field: 'source', header: 'Source' }
])
@@ -226,6 +224,7 @@ const togglableColumns = ref([
// Define initially visible columns (excluding 'mbc' and 'address')
const visibleColumns = ref(
togglableColumns.value.filter((col) => col.field !== 'mbc' && col.field !== 'address')
//togglableColumns.value.filter((col) => col.field !== 'address')
)
const onToggle = (val) => {
@@ -282,19 +281,6 @@ const toggleAll = () => {
onMounted(() => {
if (props.data && props.data.rules) {
treeData.value = parseRules(props.data.rules, props.data.meta.flavor)
expandedKeys.value = {}
treeData.value.forEach((rootNode) => {
expandedKeys.value[rootNode.key] = false
if (rootNode.children) {
rootNode.children.forEach((matchNode) => {
expandedKeys.value[matchNode.key] = false
if (matchNode.children) {
expandAllChildren(matchNode)
}
})
expandedKeys.value[rootNode.children[0].key] = true
}
})
} else {
console.error('Invalid data prop:', props.data)
}
@@ -359,6 +345,11 @@ function createMBCHref(mbc) {
visibility: hidden !important;
height: 1.3rem;
}
/* Disable the toggle button for rules */
:deep(.p-treetable-tbody > tr:is([aria-level='1']) > td > div > .p-treetable-node-toggle-button) {
visibility: collapse !important;
height: 1.3rem;
}
/* Make all matches nodes (i.e. not rule names) slightly smaller */
.p-treetable-tbody > tr:not(:is([aria-level='1'])) > td {

View File

@@ -4,7 +4,7 @@
{{ node.data.name }}
</template>
<template v-else-if="node.data.type === 'match location'">
<span class="font-italic">{{ node.data.name }}</span>
<span class="text-sm font-italic">{{ node.data.name }}</span>
</template>
<template v-else-if="node.data.type === 'statement'">-
<span :class="{ 'text-green-700': node.data.typeValue === 'range', 'font-semibold': node.data.typeValue !== 'range' }">
@@ -12,9 +12,7 @@
</span>
</template>
<template v-else-if="node.data.type === 'feature'">
<!--<span v-if="node.data.typeValue === 'number' || node.data.typeValue === 'mnemonic' || node.data.typeValue === 'bytes' || node.data.typeValue === 'api' || node.data.typeValue === 'offset' || node.data.typeValue === 'operand offset'">- {{ node.data.typeValue }}: <span class="text-green-700" style="font-family: monospace;">{{ node.data.name }}</span></span>-->
<span>- {{ node.data.typeValue }}: <span class="text-green-700" style="font-family: monospace;">{{ node.data.name }}</span></span>
</template>
<span v-if="node.data.description" class="text-gray-500 text-sm" style="font-size: 90%;">
= {{ node.data.description }}
@@ -25,15 +23,15 @@
<LibraryTag v-if="node.data.lib && node.data.matchCount" />
</div>
</template>
<script setup>
import { defineProps } from 'vue';
import LibraryTag from '../misc/LibraryTag.vue';
defineProps({
node: {
type: Object,
required: true
}
});
</script>
</script>

View File

@@ -56,7 +56,7 @@ export function parseRules(rules, flavor) {
? `${formatStaticAddress(match[0].value)}`
: formatDynamicAddress(match[0].value),
},
children: [parseNode(match[1], `${matchKey}-0`, rules, rule.meta.lib)]
children: [parseNode(match[1], `${matchKey}`, rules, rule.meta.lib)]
}
matchCounter++
return matchNode
@@ -219,8 +219,6 @@ function parseNode(node, key, rules, lib) {
return null
}
let childCounter = 0
const result = {
key: key,
data: {
@@ -241,10 +239,7 @@ function parseNode(node, key, rules, lib) {
if (processedNode.children && Array.isArray(processedNode.children)) {
result.children = processedNode.children
.map((child) => {
const childNode = parseNode(child, `${key}-${childCounter}`, rules, lib)
if (childNode) {
childCounter++
}
const childNode = parseNode(child, `${key}`, rules, lib)
return childNode
})
.filter((child) => child !== null)