From 013921e78728a464d5ddf4b545bbbd188e257fdb Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 8 Mar 2026 20:48:02 +0000 Subject: [PATCH 1/2] Switch to using ruff for formatting as well as linting --- .github/workflows/black.yml | 15 ------------- .github/workflows/ruff.yaml | 4 +++- volatility3/cli/text_renderer.py | 6 ++--- volatility3/framework/automagic/linux.py | 4 +--- volatility3/framework/layers/avml.py | 3 +-- volatility3/framework/objects/__init__.py | 6 ++--- .../plugins/linux/malware/malfind.py | 7 +++++- .../framework/plugins/linux/pidhashtable.py | 8 ++----- volatility3/framework/plugins/linux/proc.py | 6 ++--- .../plugins/linux/tracing/perf_events.py | 4 +++- .../framework/plugins/mac/proc_maps.py | 6 ++--- volatility3/framework/plugins/mac/pslist.py | 4 +++- .../plugins/windows/getservicesids.py | 4 +--- .../plugins/windows/malware/pebmasquerade.py | 4 +++- .../framework/plugins/windows/pslist.py | 22 ++++++++++--------- .../framework/plugins/windows/psscan.py | 6 ++--- .../framework/plugins/windows/pstree.py | 12 +++++----- .../framework/plugins/windows/thrdscan.py | 8 ++----- .../framework/plugins/windows/vadinfo.py | 10 +++++---- .../symbols/windows/extensions/pool.py | 6 ++--- .../framework/symbols/windows/pdbutil.py | 4 +--- 21 files changed, 67 insertions(+), 82 deletions(-) delete mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml deleted file mode 100644 index 3df690543..000000000 --- a/.github/workflows/black.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Black python formatter - -on: [push, pull_request] - -jobs: - lint: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: psf/black@stable - with: - options: "--check --diff --verbose" - src: "./volatility3" - # FIXME: Remove when Volatility3 minimum Python version is >3.8 - version: "24.8.0" diff --git a/.github/workflows/ruff.yaml b/.github/workflows/ruff.yaml index 98a05a616..e2381dab2 100644 --- a/.github/workflows/ruff.yaml +++ b/.github/workflows/ruff.yaml @@ -4,7 +4,7 @@ name: Ruff on: [push, pull_request] jobs: - lint: + lint-and-format: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -13,3 +13,5 @@ jobs: with: args: check src: "." + + - run: "ruff format --check --diff" diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index d00c00bf4..d400067af 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -464,9 +464,9 @@ class PrettyTextRenderer(CLIRenderer): accumulator.append((node.path_depth, line)) return accumulator - final_output: List[Tuple[int, Dict[interfaces.renderers.Column, list[str]]]] = ( - [] - ) + final_output: List[ + Tuple[int, Dict[interfaces.renderers.Column, list[str]]] + ] = [] if not grid.populated: grid.populate(visitor, final_output) else: diff --git a/volatility3/framework/automagic/linux.py b/volatility3/framework/automagic/linux.py index 95703aaf7..511b95731 100644 --- a/volatility3/framework/automagic/linux.py +++ b/volatility3/framework/automagic/linux.py @@ -165,9 +165,7 @@ class LinuxIntelStacker(interfaces.automagic.StackerLayerInterface): "init_mm" ).address and init_task.tasks.next.cast( "long unsigned int" - ) == init_task.tasks.prev.cast( - "long unsigned int" - ): + ) == init_task.tasks.prev.cast("long unsigned int"): # The idle task steals `mm` from previously running task, i.e., # `init_mm` is only used as long as no CPU has ever been idle. # This catches cases where we found a fragment of the diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 7c052f70a..841d821cb 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -160,8 +160,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): if frame_type == 0xFF: if ( data[ - offset - + frame_header_len : offset + offset + frame_header_len : offset + frame_header_len + frame_size ] diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index 08d6cb31e..40eb532f1 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -948,9 +948,9 @@ class AggregateType(interfaces.objects.ObjectInterface): if isinstance(cls, agg_type): agg_name = agg_type.__name__ - assert isinstance( - members, collections.abc.Mapping - ), f"{agg_name} members parameter must be a mapping: {type(members)}" + assert isinstance(members, collections.abc.Mapping), ( + f"{agg_name} members parameter must be a mapping: {type(members)}" + ) assert all( (isinstance(member, tuple) and len(member) == 2) for member in members.values() diff --git a/volatility3/framework/plugins/linux/malware/malfind.py b/volatility3/framework/plugins/linux/malware/malfind.py index cbd9f87c1..533a4e217 100644 --- a/volatility3/framework/plugins/linux/malware/malfind.py +++ b/volatility3/framework/plugins/linux/malware/malfind.py @@ -88,7 +88,12 @@ class Malfind(interfaces.plugins.PluginInterface): for page_addr in malicious_pages: offset = page_addr - vma.vm_start data = proc_layer.read(page_addr, dump_size, pad=True) - yield vma, f"{vma_name}, page address: {page_addr:#x}, offset: {offset:#x}", data, offset + yield ( + vma, + f"{vma_name}, page address: {page_addr:#x}, offset: {offset:#x}", + data, + offset, + ) else: # Original behaviour - Dump the start of the region (not necessarily matching the dirty page) data = proc_layer.read(vma.vm_start, dump_size, pad=True) diff --git a/volatility3/framework/plugins/linux/pidhashtable.py b/volatility3/framework/plugins/linux/pidhashtable.py index b4b1643e1..0324ed846 100644 --- a/volatility3/framework/plugins/linux/pidhashtable.py +++ b/volatility3/framework/plugins/linux/pidhashtable.py @@ -194,15 +194,11 @@ class PIDHashTable(plugins.PluginInterface): has_pid_numbers = vmlinux.has_type("pid") and vmlinux.get_type( "pid" - ).has_member( - "numbers" - ) # kernels >= 2.6.24 + ).has_member("numbers") # kernels >= 2.6.24 has_pid_chain = vmlinux.has_type("upid") and vmlinux.get_type( "upid" - ).has_member( - "pid_chain" - ) # 2.6.24 <= kernels < 4.15 + ).has_member("pid_chain") # 2.6.24 <= kernels < 4.15 # kernels >= 4.15 pid_idr = vmlinux.has_type("pid_namespace") and vmlinux.get_type( diff --git a/volatility3/framework/plugins/linux/proc.py b/volatility3/framework/plugins/linux/proc.py index e9a126374..2c6ebd825 100644 --- a/volatility3/framework/plugins/linux/proc.py +++ b/volatility3/framework/plugins/linux/proc.py @@ -70,9 +70,9 @@ class Maps(plugins.PluginInterface): def list_vmas( cls, task: interfaces.objects.ObjectInterface, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: True, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + True + ), ) -> Generator[interfaces.objects.ObjectInterface, None, None]: """Lists the Virtual Memory Areas of a specific process. diff --git a/volatility3/framework/plugins/linux/tracing/perf_events.py b/volatility3/framework/plugins/linux/tracing/perf_events.py index ff922784d..c7e4dbe34 100644 --- a/volatility3/framework/plugins/linux/tracing/perf_events.py +++ b/volatility3/framework/plugins/linux/tracing/perf_events.py @@ -34,7 +34,9 @@ class PerfEvents(plugins.PluginInterface): ] @classmethod - def list_perf_events(cls, context, vmlinux_module_name: str) -> Generator[ + def list_perf_events( + cls, context, vmlinux_module_name: str + ) -> Generator[ Tuple[ interfaces.objects.ObjectInterface, interfaces.objects.ObjectInterface, diff --git a/volatility3/framework/plugins/mac/proc_maps.py b/volatility3/framework/plugins/mac/proc_maps.py index 87f3559ea..b1370afdb 100644 --- a/volatility3/framework/plugins/mac/proc_maps.py +++ b/volatility3/framework/plugins/mac/proc_maps.py @@ -65,9 +65,9 @@ class Maps(interfaces.plugins.PluginInterface): def list_vmas( cls, task: interfaces.objects.ObjectInterface, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: True, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + True + ), ) -> Generator[interfaces.objects.ObjectInterface, None, None]: """Lists the Virtual Memory Areas of a specific process. diff --git a/volatility3/framework/plugins/mac/pslist.py b/volatility3/framework/plugins/mac/pslist.py index 904e4e201..f36bcd831 100644 --- a/volatility3/framework/plugins/mac/pslist.py +++ b/volatility3/framework/plugins/mac/pslist.py @@ -49,7 +49,9 @@ class PsList(interfaces.plugins.PluginInterface): ] @classmethod - def get_list_tasks(cls, method: str) -> Callable[ + def get_list_tasks( + cls, method: str + ) -> Callable[ [interfaces.context.ContextInterface, str, Callable[[int], bool]], Iterable[interfaces.objects.ObjectInterface], ]: diff --git a/volatility3/framework/plugins/windows/getservicesids.py b/volatility3/framework/plugins/windows/getservicesids.py index c04472eab..96786b586 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -19,9 +19,7 @@ vollog = logging.getLogger(__name__) def createservicesid(svc) -> str: """Calculate the Service SID""" uni = "".join([c + "\x00" for c in svc]) - sha = hashlib.sha1( - uni.upper().encode("utf-8") - ).digest() # pylint: disable-msg=E1101 + sha = hashlib.sha1(uni.upper().encode("utf-8")).digest() # pylint: disable-msg=E1101 dec = list() for i in range(5): ## The use of struct here is OK. It doesn't make much sense diff --git a/volatility3/framework/plugins/windows/malware/pebmasquerade.py b/volatility3/framework/plugins/windows/malware/pebmasquerade.py index cd898239f..867609f16 100644 --- a/volatility3/framework/plugins/windows/malware/pebmasquerade.py +++ b/volatility3/framework/plugins/windows/malware/pebmasquerade.py @@ -37,7 +37,9 @@ class PebMasquerade(interfaces.plugins.PluginInterface): ] @classmethod - def get_process_names(cls, proc: interfaces.objects.ObjectInterface) -> Tuple[ + def get_process_names( + cls, proc: interfaces.objects.ObjectInterface + ) -> Tuple[ Union[str, renderers.NotAvailableValue], Union[str, renderers.NotAvailableValue], Union[str, renderers.NotAvailableValue], diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index 1043f8b42..db3e5dc99 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -167,13 +167,15 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): Filter function for passing to the `list_processes` method """ - return lambda x: not ( - x.is_valid() - and x.ActiveThreads > 0 - and x.UniqueProcessId != 4 - and x.InheritedFromUniqueProcessId != 4 - and x.ExitTime.QuadPart == 0 - and x.get_handle_count() != renderers.UnreadableValue() + return lambda x: ( + not ( + x.is_valid() + and x.ActiveThreads > 0 + and x.UniqueProcessId != 4 + and x.InheritedFromUniqueProcessId != 4 + and x.ExitTime.QuadPart == 0 + and x.get_handle_count() != renderers.UnreadableValue() + ) ) @classmethod @@ -214,9 +216,9 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): cls, context: interfaces.context.ContextInterface, kernel_module_name: str, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: False, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + False + ), ) -> Iterator["extensions.EPROCESS"]: """Lists all the processes in the given layer that are in the pid config option. diff --git a/volatility3/framework/plugins/windows/psscan.py b/volatility3/framework/plugins/windows/psscan.py index ae37c20a1..de69c23be 100644 --- a/volatility3/framework/plugins/windows/psscan.py +++ b/volatility3/framework/plugins/windows/psscan.py @@ -150,9 +150,9 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): cls, context: interfaces.context.ContextInterface, kernel_module_name: str, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: False, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + False + ), ) -> Iterable[interfaces.objects.ObjectInterface]: """Scans for processes using the poolscanner module and constraints. diff --git a/volatility3/framework/plugins/windows/pstree.py b/volatility3/framework/plugins/windows/pstree.py index 373c555f6..c8d9a3f66 100644 --- a/volatility3/framework/plugins/windows/pstree.py +++ b/volatility3/framework/plugins/windows/pstree.py @@ -53,9 +53,9 @@ class PsTree(interfaces.plugins.PluginInterface): def find_level( self, pid: int, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: False, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + False + ), ) -> None: """Finds how deep the pid is in the processes list.""" seen = {pid} @@ -77,9 +77,9 @@ class PsTree(interfaces.plugins.PluginInterface): def _generator( self, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: False, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + False + ), ): """Generates the Tree of processes.""" kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/thrdscan.py b/volatility3/framework/plugins/windows/thrdscan.py index 1588f292e..4b2bf47d2 100644 --- a/volatility3/framework/plugins/windows/thrdscan.py +++ b/volatility3/framework/plugins/windows/thrdscan.py @@ -99,12 +99,8 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) thread_tid = ethread.Cid.UniqueThread thread_start_addr = ethread.StartAddress thread_win32start_addr = ethread.Win32StartAddress - thread_create_time = ( - ethread.get_create_time() - ) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object - thread_exit_time = ( - ethread.get_exit_time() - ) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object + thread_create_time = ethread.get_create_time() # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object + thread_exit_time = ethread.get_exit_time() # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object owner_proc = None if vads_cache is not None: diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index 22d42505f..25abd729e 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -115,9 +115,9 @@ class VadInfo(interfaces.plugins.PluginInterface): def list_vads( cls, proc: interfaces.objects.ObjectInterface, - filter_func: Callable[ - [interfaces.objects.ObjectInterface], bool - ] = lambda _: False, + filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: ( + False + ), ) -> Generator[interfaces.objects.ObjectInterface, None, None]: """Lists the Virtual Address Descriptors of a specific process. @@ -198,7 +198,9 @@ class VadInfo(interfaces.plugins.PluginInterface): return file_handle - def _generator(self, procs: List[interfaces.objects.ObjectInterface]) -> Generator[ + def _generator( + self, procs: List[interfaces.objects.ObjectInterface] + ) -> Generator[ Tuple[ int, Tuple[ diff --git a/volatility3/framework/symbols/windows/extensions/pool.py b/volatility3/framework/symbols/windows/extensions/pool.py index f12182fa7..a3bc3aefe 100644 --- a/volatility3/framework/symbols/windows/extensions/pool.py +++ b/volatility3/framework/symbols/windows/extensions/pool.py @@ -126,8 +126,7 @@ class POOL_HEADER(objects.StructType): infomask_value = infomask_data[addr + infomask_offset] pointercount_value = int.from_bytes( infomask_data[ - addr - + pointercount_offset : addr + addr + pointercount_offset : addr + pointercount_offset + pointercount_size ], @@ -165,8 +164,7 @@ class POOL_HEADER(objects.StructType): (padding_length,) = struct.unpack( " Date: Mon, 9 Mar 2026 20:47:24 +0000 Subject: [PATCH 2/2] Update documentation to remove black and use ruff --- CODING_STYLE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index a4e248ffe..ed69003c6 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -5,7 +5,7 @@ The coding standards for volatility are mostly by our linter and our code format All code submissions will be vetted automatically through tests from both and the submission will not be accepted if either of these fail. Code Linter: Ruff -Code Formatter: Black +Code Formatter: Ruff In addition, there are some coding practices that we employ to prevent specific failure cases and ensure consistency across the codebase. These are documented below along with the rationale for the decision.