diff --git a/webui/src/components/RuleMatchesTable.vue b/webui/src/components/RuleMatchesTable.vue index b60afe0df..5490298f6 100644 --- a/webui/src/components/RuleMatchesTable.vue +++ b/webui/src/components/RuleMatchesTable.vue @@ -1,11 +1,11 @@ - - + + @@ -149,10 +129,8 @@ import IconField from 'primevue/iconfield' import InputIcon from 'primevue/inputicon' import ContextMenu from 'primevue/contextmenu' - -import RuleColumn from './columns/RuleColumn.vue'; -import VTIcon from './misc/VTIcon.vue'; - +import RuleColumn from './columns/RuleColumn.vue' +import VTIcon from './misc/VTIcon.vue' import { parseRules } from '../utils/rdocParser' @@ -174,8 +152,8 @@ const sourceDialogVisible = ref(false) const currentSource = ref('') const expandedKeys = ref({}) -const menu = ref(); -const selectedNode = ref({}); +const menu = ref() +const selectedNode = ref({}) const contextMenuItems = computed(() => [ { @@ -183,55 +161,55 @@ const contextMenuItems = computed(() => [ icon: 'pi pi-eye', command: () => { showSource(selectedNode.value.data.source) - }, + } }, { label: 'View rule in capa-rules', icon: 'pi pi-external-link', target: '_blank', - url: selectedNode.value.url, + url: selectedNode.value.url }, { label: 'Lookup rule in VirusTotal', icon: 'vt-icon', target: '_blank', - url: selectedNode.value.vturl, - }, -]); + url: selectedNode.value.vturl + } +]) const onRightClick = (event, instance) => { if (instance.node.data.source) { - selectedNode.value = instance.node; + selectedNode.value = instance.node // contrust capa-rules url selectedNode.value.url = `https://github.com/mandiant/capa-rules/blob/master/${instance.node.data.namespace || 'lib'}/${instance.node.data.name.toLowerCase().replace(/\s+/g, '-')}.yml` // construct VirusTotal deep link - const behaviourSignature = `behaviour_signature:"${instance.node.data.name}"`; + const behaviourSignature = `behaviour_signature:"${instance.node.data.name}"` selectedNode.value.vturl = `https://www.virustotal.com/gui/search/${behaviourSignature}/files` - menu.value.show(event); + menu.value.show(event) } -}; +} -/* - * Expand node on click +/* + * Expand node on click */ const onNodeSelect = (node) => { - const nodeKey = node.key; - const nodeType = node.data.type; + 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 }; + // 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}; + const [parentKey, _] = nodeKey.split('-') + expandedKeys.value = { [parentKey]: true, [`${nodeKey}`]: true } } else { return } -}; +} // All available columns const togglableColumns = ref([ @@ -272,10 +250,9 @@ const showSource = (source) => { sourceDialogVisible.value = true } - onMounted(() => { if (props.data && props.data.rules) { - treeData.value = parseRules(props.data.rules, props.data.meta.flavor) + treeData.value = parseRules(props.data.rules, props.data.meta.flavor, props.data.meta.analysis.layout) } else { console.error('Invalid data prop:', props.data) } @@ -289,25 +266,25 @@ onMounted(() => { */ function createMBCHref(mbc) { - let baseUrl; + 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'; + 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'; + baseUrl = 'https://github.com/MBCProject/mbc-markdown/blob/main/micro-behaviors' } else { return null } // Convert the objective and behavior to lowercase and replace spaces with hyphens - const objectivePath = mbc.objective.toLowerCase().replace(/\s+/g, '-'); - const behaviorPath = mbc.behavior.toLowerCase().replace(/\s+/g, '-'); + const objectivePath = mbc.objective.toLowerCase().replace(/\s+/g, '-') + const behaviorPath = mbc.behavior.toLowerCase().replace(/\s+/g, '-') // Construct the final URL - return `${baseUrl}/${objectivePath}/${behaviorPath}.md`; + return `${baseUrl}/${objectivePath}/${behaviorPath}.md` } /** @@ -317,33 +294,34 @@ function createMBCHref(mbc) { * @param {string} attack.id - The ID of the ATT&CK technique or sub-technique. * @returns {string} The formatted MITRE ATT&CK URL for the technique. */ - function createATTACKHref(attack) { - const baseUrl = 'https://attack.mitre.org/techniques/'; - const idParts = attack.id.split('.'); +function createATTACKHref(attack) { + const baseUrl = 'https://attack.mitre.org/techniques/' + const idParts = attack.id.split('.') if (idParts.length === 1) { // It's a technique - return `${baseUrl}${idParts[0]}`; + return `${baseUrl}${idParts[0]}` } else if (idParts.length === 2) { // It's a sub-technique - return `${baseUrl}${idParts[0]}/${idParts[1]}`; + return `${baseUrl}${idParts[0]}/${idParts[1]}` } else { return null } } -