diff --git a/dissect/target/plugin.py b/dissect/target/plugin.py index 55a2371e6b..74781cb465 100644 --- a/dissect/target/plugin.py +++ b/dissect/target/plugin.py @@ -459,18 +459,36 @@ def __call__(self, *args, **kwargs) -> Iterator[Record | Any]: self.target.log.debug("", exc_info=e) def get_paths(self) -> Iterator[Path]: + """Return all artifact paths.""" if self.target.is_direct: yield from self._get_paths_direct() else: yield from self._get_paths() + def get_all_paths(self) -> Iterator[Path]: + """Return all artifact and auxiliary paths. + + The implementation of this function will + probably change in the future, but the interface + should stay the same. + """ + yield from self.get_paths() + yield from self._get_auxiliary_paths() + def _get_paths_direct(self) -> Iterator[Path]: """Return all paths as given by the user.""" for path in self.target._loader.paths: yield self.target.fs.path(str(path)) def _get_paths(self) -> Iterator[Path]: - """Return all files of interest to the plugin. + """Return all artifact files of interest to the plugin. + + To be implemented by the plugin subclass. + """ + raise NotImplementedError + + def _get_auxiliary_paths(self) -> Iterator[Path]: + """Return all auxiliary files of interest to the plugin. To be implemented by the plugin subclass. """ diff --git a/dissect/target/plugins/apps/webserver/apache.py b/dissect/target/plugins/apps/webserver/apache.py index 9d61b10116..5a3181d3dc 100644 --- a/dissect/target/plugins/apps/webserver/apache.py +++ b/dissect/target/plugins/apps/webserver/apache.py @@ -4,6 +4,7 @@ import re from datetime import datetime from functools import cached_property +from pathlib import Path from typing import TYPE_CHECKING, NamedTuple from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError @@ -18,7 +19,6 @@ if TYPE_CHECKING: from collections.abc import Iterator - from pathlib import Path from dissect.target.target import Target @@ -240,6 +240,7 @@ def __init__(self, target: Target): self.access_paths = set() self.error_paths = set() self.virtual_hosts = set() + self.resolved_config_paths = set() self.find_logs() def check_compatible(self) -> None: @@ -251,7 +252,8 @@ def check_compatible(self) -> None: def find_logs(self) -> None: """Discover any present Apache log paths on the target system. - Populates ``self.access_paths``, ``self.error_paths`` and ``self.virtual_hosts``. + Populates ``self.access_paths``, ``self.error_paths``, + ``self.virtual_hosts`` and ``self.resolved_config_paths``. References: - https://httpd.apache.org/docs/2.4/logs.html @@ -280,6 +282,18 @@ def find_logs(self) -> None: if path not in seen: self._process_conf_file(path, seen) + def _get_paths(self) -> Iterator[Path]: + yield from self.access_paths | self.error_paths + + def _get_auxiliary_paths(self) -> Iterator[Path]: + config_paths = set() + for path in self.DEFAULT_CONFIG_PATHS: + config_paths.add(Path(path)) + + config_paths.update(self.resolved_config_paths) + + yield from config_paths + def _process_conf_file(self, path: Path, seen: set[Path] | None = None) -> None: """Process an Apache ``.conf`` file for ``ServerRoot``, ``CustomLog``, ``Include`` and ``OptionalInclude`` directives. Populates ``self.access_paths`` and ``self.error_paths``. diff --git a/dissect/target/plugins/apps/webserver/caddy.py b/dissect/target/plugins/apps/webserver/caddy.py index acbaa13a55..0171bad427 100644 --- a/dissect/target/plugins/apps/webserver/caddy.py +++ b/dissect/target/plugins/apps/webserver/caddy.py @@ -3,6 +3,7 @@ import json import re from datetime import datetime +from pathlib import Path from typing import TYPE_CHECKING from dissect.util.ts import from_unix @@ -17,7 +18,6 @@ if TYPE_CHECKING: from collections.abc import Iterator - from pathlib import Path from dissect.target.target import Target @@ -32,6 +32,8 @@ class CaddyPlugin(WebserverPlugin): __namespace__ = "caddy" + DEFAULT_CONFIG_PATH = "/etc/caddy/Caddyfile" + def __init__(self, target: Target): super().__init__(target) self.log_paths = self.get_log_paths() @@ -47,7 +49,7 @@ def get_log_paths(self) -> list[Path]: log_paths.extend(self.target.fs.path("/var/log").glob("caddy_access.log*")) # Check for custom paths in Caddy config - if (config_file := self.target.fs.path("/etc/caddy/Caddyfile")).exists(): + if (config_file := self.target.fs.path(self.DEFAULT_CONFIG_PATH)).exists(): found_roots = [] for line in config_file.open("rt"): line = line.strip() @@ -95,6 +97,12 @@ def get_log_paths(self) -> list[Path]: return log_paths + def _get_paths(self) -> Iterator[Path]: + yield from self.log_paths + + def _get_auxiliary_paths(self) -> Iterator[Path]: + yield from {Path(self.DEFAULT_CONFIG_PATH)} + @export(record=WebserverAccessLogRecord) def access(self) -> Iterator[WebserverAccessLogRecord]: """Parses Caddy V1 CRF and Caddy V2 JSON access logs. diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index afd70f78c1..1cbbb2af8c 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -125,6 +125,13 @@ def log_dirs(self) -> dict[str, set[Path]]: return dirs + def _get_paths(self) -> Iterator[Path]: + for path in self.log_dirs.values(): + yield from path + + def _get_auxiliary_paths(self) -> Iterator[Path]: + yield from {self.config} + @export(record=BasicRecordDescriptor) def logs(self) -> Iterator[TargetRecordDescriptor]: """Return contents of IIS (v7 and above) log files. @@ -147,8 +154,7 @@ def logs(self) -> Iterator[TargetRecordDescriptor]: self.target.log.info("Processing IIS log file %s in %s format", log_file, format) yield from parsers[format](self.target, log_file) - # We don't implement _get_paths() in the IIS plugin because there's little use for it for the way the plugin - # is currently implemented. So handle direct files here. + # We handle direct files here because _get_paths cannot select (filter) on the type of logfile. if self.target.is_direct: for log_file in self.get_paths(): yield from parse_autodetect_format_log(self.target, log_file) diff --git a/dissect/target/plugins/apps/webserver/nginx.py b/dissect/target/plugins/apps/webserver/nginx.py index a9bf62764e..2eeaa850f2 100644 --- a/dissect/target/plugins/apps/webserver/nginx.py +++ b/dissect/target/plugins/apps/webserver/nginx.py @@ -106,6 +106,7 @@ def __init__(self, target: Target): self.access_paths = set() self.error_paths = set() self.host_paths = set() + self.config_paths = set() self.find_logs() @@ -127,11 +128,19 @@ def find_logs(self) -> None: if "*" in config_file: base, _, glob = config_file.partition("*") for f in self.target.fs.path(base).rglob(f"*{glob}"): + self.config_paths.add(f) self.parse_config(f) elif (config_file := self.target.fs.path(config_file)).exists(): + self.config_paths.add(config_file) self.parse_config(config_file) + def _get_paths(self) -> Iterator[Path]: + yield from self.access_paths | self.error_paths + + def _get_auxiliary_paths(self) -> Iterator[Path]: + yield from self.config_paths + def parse_config(self, path: Path, seen: set[Path] | None = None) -> None: """Parse the given NGINX ``.conf`` file for ``access_log``, ``error_log`` and ``include`` directives."""