Skip to content

Commit

Permalink
Merge branch 'master' into yelhamer-filtered-scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
yelhamer authored Aug 19, 2024
2 parents e4836e5 + c409b2b commit 9243334
Show file tree
Hide file tree
Showing 18 changed files with 279 additions and 192 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@

### Breaking Changes

### New Rules (3)
### New Rules (5)

- nursery/upload-file-to-onedrive [email protected] [email protected]
- data-manipulation/encoding/base64/decode-data-using-base64-via-vbmi-lookup-table [email protected]
- communication/socket/attach-bpf-to-socket-on-linux [email protected]
- anti-analysis/anti-av/overwrite-dll-text-section-to-remove-hooks [email protected]
- nursery/delete-file-on-linux [email protected]
-

### Bug Fixes
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ dev = [
"flake8-use-pathlib==0.3.0",
"flake8-copyright==0.2.4",
"ruff==0.5.6",
"black==24.4.2",
"black==24.8.0",
"isort==5.13.2",
"mypy==1.11.1",
"mypy-protobuf==3.6.0",
Expand All @@ -147,8 +147,8 @@ dev = [
"types-PyYAML==6.0.8",
"types-tabulate==0.9.0.20240106",
"types-termcolor==1.1.4",
"types-psutil==5.8.23",
"types_requests==2.32.0.20240602",
"types-psutil==6.0.0.20240621",
"types_requests==2.32.0.20240712",
"types-protobuf==5.27.0.20240626",
"deptry==0.17.0"
]
Expand All @@ -157,7 +157,7 @@ build = [
# we want all developer environments to be consistent.
# These dependencies are not used in production environments
# and should not conflict with other libraries/tooling.
"pyinstaller==6.9.0",
"pyinstaller==6.10.0",
"setuptools==70.0.0",
"build==1.2.1"
]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mdurl==0.1.2
msgpack==1.0.8
networkx==3.1
pefile==2023.2.7
pip==24.1.2
pip==24.2
protobuf==5.27.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
Expand Down
1 change: 1 addition & 0 deletions web/explorer/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<template>
<Toast position="bottom-center" group="bc" />
<header>
<div class="wrapper">
<BannerHeader />
Expand Down
69 changes: 38 additions & 31 deletions web/explorer/src/components/FunctionCapabilities.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
size="small"
:filters="filters"
:filterMode="filterMode"
:globalFilterFields="['funcaddr', 'ruleName', 'namespace']"
filterDisplay="row"
:globalFilterFields="['address', 'rule', 'namespace']"
>
<template #header>
<IconField>
Expand All @@ -16,35 +17,47 @@
</IconField>
</template>

<Column field="address" sortable header="Function Address" :rowspan="3" class="w-min">
<Column
field="address"
sortable
header="Function Address"
class="w-min"
:showFilterMenu="false"
:showClearButton="false"
>
<template #filter v-if="props.showColumnFilters">
<InputText v-model="filters['address'].value" placeholder="Filter by function address" />
</template>
<template #body="{ data }">
<span class="font-monospace">{{ data.address }}</span>
<span class="font-monospace text-base">{{ data.address }}</span>
<span v-if="data.matchCount > 1" class="font-italic">
({{ data.matchCount }} match{{ data.matchCount > 1 ? "es" : "" }})
</span>
</template>
</Column>

<Column field="rule" sortable header="Matches" class="w-min">
<Column field="rule" header="Rule Matches" class="w-min" :showFilterMenu="false" :showClearButton="false">
<template #filter v-if="props.showColumnFilters">
<InputText v-model="filters['rule'].value" placeholder="Filter by rule" />
</template>
<template #body="{ data }">
{{ data.rule }}
<LibraryTag v-if="data.lib" />
</template>
</Column>

<Column field="namespace" sortable header="Namespace"></Column>
<Column field="namespace" header="Namespace" :showFilterMenu="false" :showClearButton="false">
<template #filter v-if="props.showColumnFilters">
<InputText v-model="filters['namespace'].value" placeholder="Filter by namespace" />
</template>
</Column>
</DataTable>

<Dialog v-model:visible="sourceDialogVisible" :style="{ width: '50vw' }">
<highlightjs lang="yml" :code="currentSource" class="bg-white" />
</Dialog>
</template>

<script setup>
import { ref, computed, onMounted } from "vue";
import DataTable from "primevue/datatable";
import Column from "primevue/column";
import Dialog from "primevue/dialog";
import IconField from "primevue/iconfield";
import InputIcon from "primevue/inputicon";
import InputText from "primevue/inputtext";
Expand All @@ -60,33 +73,25 @@ const props = defineProps({
showLibraryRules: {
type: Boolean,
default: false
},
showColumnFilters: {
type: Boolean,
default: false
}
});
const filters = ref({ global: { value: null, matchMode: "contains" } });
const filters = ref({
global: { value: null, matchMode: "contains" },
address: { value: null, matchMode: "contains" },
rule: { value: null, matchMode: "contains" },
namespace: { value: null, matchMode: "contains" }
});
const filterMode = ref("lenient");
const sourceDialogVisible = ref(false);
const currentSource = ref("");
const functionCapabilities = ref([]);
onMounted(() => {
const cacheKey = "functionCapabilities";
let cachedData = sessionStorage.getItem(cacheKey);
if (cachedData) {
// If the data is already in sessionStorage, parse it and use it
functionCapabilities.value = JSON.parse(cachedData);
} else {
// Parse function capabilities and cache the result in sessionStorage
functionCapabilities.value = parseFunctionCapabilities(props.data);
try {
sessionStorage.setItem(cacheKey, JSON.stringify(functionCapabilities.value));
} catch (e) {
console.warn("Failed to store parsed data in sessionStorage:", e);
// If storing fails (e.g., due to storage limits), we can still continue with the parsed data
}
}
functionCapabilities.value = parseFunctionCapabilities(props.data);
});
/*
Expand Down Expand Up @@ -116,8 +121,10 @@ const tableData = computed(() => {
</script>

<style scoped>
/* tighten up the spacing between rows */
:deep(.p-datatable.p-datatable-sm .p-datatable-tbody > tr > td) {
/* tighten up the spacing between rows, and change border color */
:deep(.p-datatable-tbody > tr > td) {
padding: 0.2rem 0.5rem !important;
border-width: 0 0 1px 0;
border-color: #97a0ab;
}
</style>
7 changes: 6 additions & 1 deletion web/explorer/src/components/NavBar.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
<script setup>
import Menubar from "primevue/menubar";
import { RouterLink } from "vue-router";
</script>

<template>
<Menubar class="p-1">
<template #start>
<RouterLink to="/">
<img src="@/assets/images/icon.png" alt="Logo" class="w-2rem" />
</RouterLink>
</template>
<template #end>
<div class="flex align-items-center gap-3">
<a
Expand All @@ -18,7 +24,6 @@ import Menubar from "primevue/menubar";
<a v-ripple href="https://github.com/mandiant/capa" class="flex justify-content-center w-2rem">
<i class="pi pi-github text-2xl"></i>
</a>
<img src="@/assets/images/icon.png" alt="Logo" class="w-2rem" />
</div>
</template>
</Menubar>
Expand Down
2 changes: 1 addition & 1 deletion web/explorer/src/components/ProcessCapabilities.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
filterMode="lenient"
sortField="pid"
:sortOrder="1"
rowHover="true"
:rowHover="true"
>
<Column field="processname" header="Process" expander>
<template #body="slotProps">
Expand Down
34 changes: 10 additions & 24 deletions web/explorer/src/components/RuleMatchesTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,11 @@
<span v-if="item.icon !== 'vt-icon'" :class="item.icon" />
<VTIcon v-else-if="item.icon === 'vt-icon'" />
<span>{{ item.label }}</span>
<i v-if="item.description" class="pi pi-info-circle text-xs" v-tooltip.right="item.description" />
</a>
</template>
</ContextMenu>

<Toast />

<!-- Source code dialog -->
<Dialog v-model:visible="sourceDialogVisible" style="width: 50vw">
<highlightjs autodetect :code="currentSource" />
Expand Down Expand Up @@ -217,6 +216,13 @@ const expandedKeys = ref({});
const menu = ref();
const selectedNode = ref({});
const contextMenuItems = computed(() => [
{
label: "Copy rule name",
icon: "pi pi-copy",
command: () => {
navigator.clipboard.writeText(selectedNode.value.data?.name);
}
},
{
label: "View source",
icon: "pi pi-eye",
Expand All @@ -234,6 +240,7 @@ const contextMenuItems = computed(() => [
label: "Lookup rule in VirusTotal",
icon: "vt-icon",
target: "_blank",
description: "Requires VirusTotal Premium account",
url: createVirusTotalUrl(selectedNode.value.data?.name)
}
]);
Expand Down Expand Up @@ -325,23 +332,7 @@ const showSource = (source) => {
};
onMounted(() => {
const cacheKey = "ruleMatches";
const cachedData = sessionStorage.getItem(cacheKey);
if (cachedData) {
// If cached data exists, parse and use it
treeData.value = JSON.parse(cachedData);
} else {
// If no cached data, parse the rules and store in sessionStorage
treeData.value = parseRules(props.data.rules, props.data.meta.flavor, props.data.meta.analysis.layout);
// Store the parsed data in sessionStorage
try {
sessionStorage.setItem(cacheKey, JSON.stringify(treeData.value));
} catch (e) {
console.warn("Failed to store parsed data in sessionStorage:", e);
// If storing fails (e.g., due to storage limits), we can still continue with the parsed data
}
}
treeData.value = parseRules(props.data.rules, props.data.meta.flavor, props.data.meta.analysis.layout);
});
</script>
Expand All @@ -357,11 +348,6 @@ onMounted(() => {
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,
and tighten up the spacing between the rows */
Expand Down
7 changes: 4 additions & 3 deletions web/explorer/src/components/SettingsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
v-model="showLibraryRules"
inputId="showLibraryRules"
:binary="true"
:disabled="showNamespaceChart"
:disabled="showNamespaceChart || libraryRuleMatchesCount === 0"
/>
<label for="showLibraryRules">
<span v-if="libraryRuleMatchesCount > 1">
Show {{ libraryRuleMatchesCount }} library rule matches
Show {{ libraryRuleMatchesCount }} distinct library rules
</span>
<span v-else>Show 1 library rule match</span>
<span v-else-if="libraryRuleMatchesCount === 1">Show 1 distinct library rule</span>
<span v-else>No library rules matched</span>
</label>
</div>
<div class="flex flex-row align-items-center gap-2">
Expand Down
18 changes: 17 additions & 1 deletion web/explorer/src/components/columns/RuleColumn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@
<template v-else-if="node.data.type === 'feature'">
<span>
- {{ node.data.typeValue }}:
<span :class="{ 'text-green-700': node.data.typeValue !== 'regex' }" class="font-monospace">
<span
:class="{ 'text-green-700': node.data.typeValue !== 'regex' }"
class="font-monospace"
v-tooltip.top="{
value: getTooltipContent(node.data),
showDelay: 1000,
hideDelay: 300
}"
>
{{ node.data.name }}
</span>
</span>
Expand Down Expand Up @@ -63,4 +71,12 @@ defineProps({
required: true
}
});
const getTooltipContent = (data) => {
if (data.typeValue === "number" || data.typeValue === "offset") {
const decimalValue = parseInt(data.name, 16);
return `Decimal: ${decimalValue}`;
}
return null;
};
</script>
Loading

0 comments on commit 9243334

Please sign in to comment.