Skip to content

Commit 5302e96

Browse files
author
Dave Lassalle
committed
Merge branch 'develop' into 816-port-cmdscan-and-console-plugins-from-vol2-to-vol3-please
2 parents 131fb21 + 055f2e7 commit 5302e96

40 files changed

+3602
-212
lines changed

.github/workflows/build-pypi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
runs-on: ubuntu-20.04
1919
strategy:
2020
matrix:
21-
python-version: ["3.7"]
21+
python-version: ["3.8"]
2222
steps:
2323
- uses: actions/checkout@v4
2424
- name: Set up Python ${{ matrix.python-version }}

.github/workflows/install.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
fail-fast: false
99
matrix:
1010
host: [ ubuntu-latest, windows-latest ]
11-
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
11+
python-version: [ "3.8", "3.9", "3.10", "3.11" ]
1212
steps:
1313
- uses: actions/checkout@v4
1414

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
runs-on: ubuntu-20.04
77
strategy:
88
matrix:
9-
python-version: ["3.7"]
9+
python-version: ["3.8"]
1010
steps:
1111
- uses: actions/checkout@v4
1212
- name: Set up Python ${{ matrix.python-version }}
@@ -46,7 +46,7 @@ jobs:
4646
4747
- name: Clean up post-test
4848
run: |
49-
rm -rf *.lime
49+
rm -rf *.bin
5050
rm -rf *.img
5151
cd volatility3/symbols
5252
rm -rf linux

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ more details.
2020

2121
## Requirements
2222

23-
Volatility 3 requires Python 3.7.3 or later. To install the most minimal set of dependencies (some plugins will not work) use a command such as:
23+
Volatility 3 requires Python 3.8.0 or later. To install the most minimal set of dependencies (some plugins will not work) use a command such as:
2424

2525
```shell
2626
pip3 install -r requirements-minimal.txt

doc/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ sphinx_autodoc_typehints>=1.4.0
44
sphinx-rtd-theme>=0.4.3
55

66
yara-python
7+
yara-x
78
pycryptodome
89
pefile

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
authors = [
77
{ name = "Volatility Foundation", email = "[email protected]" },
88
]
9-
requires-python = ">=3.7.3"
9+
requires-python = ">=3.8.0"
1010
license = { text = "VSL" }
1111
dynamic = ["dependencies", "optional-dependencies", "version"]
1212

test/requirements-testing.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ pefile>=2017.8.1 #foo
66

77
# This is required for the yara plugins
88
yara-python>=3.8.0
9+
yara-x>=0.5.0
910

1011
pytest>=7.0.0

volatility3/cli/text_renderer.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,14 @@ def render(self, grid: interfaces.renderers.TreeGrid) -> None:
179179
outfd.write("\n{}\n".format("\t".join(line)))
180180

181181
def visitor(node: interfaces.renderers.TreeNode, accumulator):
182-
if self.filter and self.filter.filter(node.values):
182+
line = []
183+
for column_index, column in enumerate(grid.columns):
184+
renderer = self._type_renderers.get(
185+
column.type, self._type_renderers["default"]
186+
)
187+
line.append(renderer(node.values[column_index]))
188+
189+
if self.filter and self.filter.filter(line):
183190
return accumulator
184191

185192
accumulator.write("\n")
@@ -188,13 +195,6 @@ def visitor(node: interfaces.renderers.TreeNode, accumulator):
188195
"*" * max(0, node.path_depth - 1)
189196
+ ("" if (node.path_depth <= 1) else " ")
190197
)
191-
line = []
192-
for column_index in range(len(grid.columns)):
193-
column = grid.columns[column_index]
194-
renderer = self._type_renderers.get(
195-
column.type, self._type_renderers["default"]
196-
)
197-
line.append(renderer(node.values[column_index]))
198198
accumulator.write("{}".format("\t".join(line)))
199199
accumulator.flush()
200200
return accumulator
@@ -259,12 +259,17 @@ def render(self, grid: interfaces.renderers.TreeGrid) -> None:
259259
def visitor(node: interfaces.renderers.TreeNode, accumulator):
260260
# Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case
261261
row = {"TreeDepth": str(max(0, node.path_depth - 1))}
262-
for column_index in range(len(grid.columns)):
263-
column = grid.columns[column_index]
262+
line = []
263+
for column_index, column in enumerate(grid.columns):
264264
renderer = self._type_renderers.get(
265265
column.type, self._type_renderers["default"]
266266
)
267267
row[f"{column.name}"] = renderer(node.values[column_index])
268+
line.append(row[f"{column.name}"])
269+
270+
if self.filter and self.filter.filter(line):
271+
return accumulator
272+
268273
accumulator.writerow(row)
269274
return accumulator
270275

@@ -317,12 +322,9 @@ def visitor(
317322
max_column_widths.get(tree_indent_column, 0), node.path_depth
318323
)
319324

320-
if self.filter and self.filter.filter(node.values):
321-
return accumulator
322-
323325
line = {}
324-
for column_index in range(len(grid.columns)):
325-
column = grid.columns[column_index]
326+
rendered_line = []
327+
for column_index, column in enumerate(grid.columns):
326328
renderer = self._type_renderers.get(
327329
column.type, self._type_renderers["default"]
328330
)
@@ -334,6 +336,11 @@ def visitor(
334336
max_column_widths.get(column.name, len(column.name)), field_width
335337
)
336338
line[column] = data.split("\n")
339+
rendered_line.append(data)
340+
341+
if self.filter and self.filter.filter(rendered_line):
342+
return accumulator
343+
337344
accumulator.append((node.path_depth, line))
338345
return accumulator
339346

@@ -347,8 +354,7 @@ def visitor(
347354
format_string_list = [
348355
"{0:<" + str(max_column_widths.get(tree_indent_column, 0)) + "s}"
349356
]
350-
for column_index in range(len(grid.columns)):
351-
column = grid.columns[column_index]
357+
for column_index, column in enumerate(grid.columns):
352358
format_string_list.append(
353359
"{"
354360
+ str(column_index + 1)
@@ -437,15 +443,20 @@ def visitor(
437443
# Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case
438444
acc_map, final_tree = accumulator
439445
node_dict: Dict[str, Any] = {"__children": []}
440-
for column_index in range(len(grid.columns)):
441-
column = grid.columns[column_index]
446+
line = []
447+
for column_index, column in enumerate(grid.columns):
442448
renderer = self._type_renderers.get(
443449
column.type, self._type_renderers["default"]
444450
)
445451
data = renderer(list(node.values)[column_index])
446452
if isinstance(data, interfaces.renderers.BaseAbsentValue):
447453
data = None
448454
node_dict[column.name] = data
455+
line.append(data)
456+
457+
if self.filter and self.filter.filter(line):
458+
return accumulator
459+
449460
if node.parent:
450461
acc_map[node.parent.path]["__children"].append(node_dict)
451462
else:

volatility3/framework/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88
import zipfile
99

10-
required_python_version = (3, 7, 3)
10+
required_python_version = (3, 8, 0)
1111
if (
1212
sys.version_info.major != required_python_version[0]
1313
or sys.version_info.minor < required_python_version[1]

volatility3/framework/configuration/requirements.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,12 +527,14 @@ class VersionRequirement(interfaces.configuration.RequirementInterface):
527527
def __init__(
528528
self,
529529
name: str,
530-
description: str = None,
530+
description: Optional[str] = None,
531531
default: bool = False,
532532
optional: bool = False,
533533
component: Type[interfaces.configuration.VersionableInterface] = None,
534534
version: Optional[Tuple[int, ...]] = None,
535535
) -> None:
536+
if description is None:
537+
description = f"Version {'.'.join([str(x) for x in version])} dependency on {component.__module__}.{component.__name__} unmet"
536538
super().__init__(
537539
name=name, description=description, default=default, optional=optional
538540
)
@@ -544,15 +546,51 @@ def __init__(
544546
self._version = version
545547

546548
def unsatisfied(
547-
self, context: interfaces.context.ContextInterface, config_path: str
549+
self,
550+
context: interfaces.context.ContextInterface,
551+
config_path: str,
552+
accumulator: Optional[
553+
List[interfaces.configuration.VersionableInterface]
554+
] = None,
548555
) -> Dict[str, interfaces.configuration.RequirementInterface]:
549556
# Mypy doesn't appreciate our classproperty implementation, self._plugin.version has no type
550557
config_path = interfaces.configuration.path_join(config_path, self.name)
551558
if not self.matches_required(self._version, self._component.version):
552559
return {config_path: self}
560+
561+
recurse = True
562+
if accumulator is None:
563+
accumulator = set([self._component])
564+
else:
565+
if self._component in accumulator:
566+
recurse = False
567+
else:
568+
accumulator.add(self._component)
569+
570+
# Check for child requirements
571+
if (
572+
issubclass(self._component, interfaces.configuration.ConfigurableInterface)
573+
and recurse
574+
):
575+
result = {}
576+
for requirement in self._component.get_requirements():
577+
if not requirement.optional and isinstance(
578+
requirement, VersionRequirement
579+
):
580+
result.update(
581+
requirement.unsatisfied(
582+
context, config_path, accumulator.copy()
583+
)
584+
)
585+
586+
if result:
587+
result.update({config_path: self})
588+
return result
589+
553590
context.config[interfaces.configuration.path_join(config_path, self.name)] = (
554591
True
555592
)
593+
556594
return {}
557595

558596
@classmethod

0 commit comments

Comments
 (0)