Skip to content

Commit

Permalink
Improve Linux OS detection (#809)
Browse files Browse the repository at this point in the history
Co-authored-by: Schamper <[email protected]>
  • Loading branch information
Horofic and Schamper committed Sep 3, 2024
1 parent 95f27ae commit 38c0145
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 27 deletions.
8 changes: 3 additions & 5 deletions dissect/target/plugins/os/unix/bsd/freebsd/_os.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from __future__ import annotations

from typing import Optional

from dissect.target.filesystem import Filesystem
from dissect.target.plugin import export
from dissect.target.plugins.os.unix.bsd._os import BsdPlugin
Expand All @@ -14,13 +12,13 @@ def __init__(self, target: Target):
self._os_release = self._parse_os_release("/bin/freebsd-version*")

@classmethod
def detect(cls, target: Target) -> Optional[Filesystem]:
def detect(cls, target: Target) -> Filesystem | None:
for fs in target.filesystems:
if fs.exists("/net") or fs.exists("/.sujournal"):
if fs.exists("/net") and (fs.exists("/.sujournal") or fs.exists("/entropy")):
return fs

return None

@export(property=True)
def version(self) -> Optional[str]:
def version(self) -> str | None:
return self._os_release.get("USERLAND_VERSION")
24 changes: 11 additions & 13 deletions dissect/target/plugins/os/unix/linux/_os.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
from typing import Optional

from dissect.target.filesystem import Filesystem
from dissect.target.helpers.network_managers import (
Expand All @@ -8,6 +9,8 @@
)
from dissect.target.plugin import OperatingSystem, export
from dissect.target.plugins.os.unix._os import UnixPlugin
from dissect.target.plugins.os.unix.bsd.osx._os import MacPlugin
from dissect.target.plugins.os.windows._os import WindowsPlugin
from dissect.target.target import Target

log = logging.getLogger(__name__)
Expand All @@ -20,17 +23,13 @@ def __init__(self, target: Target):
self.network_manager.discover()

@classmethod
def detect(cls, target: Target) -> Optional[Filesystem]:
def detect(cls, target: Target) -> Filesystem | None:
for fs in target.filesystems:
if (
fs.exists("/var")
and fs.exists("/etc")
and fs.exists("/opt")
or (fs.exists("/sys") or fs.exists("/proc"))
and not fs.exists("/Library")
):
(fs.exists("/var") and fs.exists("/etc") and fs.exists("/opt"))
or (fs.exists("/sys/module") or fs.exists("/proc/sys"))
) and not (MacPlugin.detect(target) or WindowsPlugin.detect(target)):
return fs
return None

@export(property=True)
def ips(self) -> list[str]:
Expand Down Expand Up @@ -68,7 +67,7 @@ def netmask(self) -> list[str]:
return self.network_manager.get_config_value("netmask")

@export(property=True)
def version(self) -> str:
def version(self) -> str | None:
distrib_description = self._os_release.get("DISTRIB_DESCRIPTION", "")
name = self._os_release.get("NAME", "") or self._os_release.get("DISTRIB_ID", "")
version = (
Expand All @@ -78,10 +77,9 @@ def version(self) -> str:
)

if len(f"{name} {version}") > len(distrib_description):
return f"{name} {version}"
distrib_description = f"{name} {version}"

else:
return distrib_description
return distrib_description or None

@export(property=True)
def os(self) -> str:
Expand Down
12 changes: 10 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import pathlib
import tempfile
import textwrap
from io import BytesIO
from typing import Callable, Iterator, Optional
from typing import Callable, Iterator

import pytest

Expand Down Expand Up @@ -50,7 +52,7 @@ def make_mock_target(tmp_path: pathlib.Path) -> Iterator[Target]:
def make_os_target(
tmp_path: pathlib.Path,
os_plugin: type[OSPlugin],
root_fs: Optional[Filesystem] = None,
root_fs: Filesystem | None = None,
apply_target: bool = True,
) -> Target:
mock_target = next(make_mock_target(tmp_path))
Expand Down Expand Up @@ -126,6 +128,12 @@ def fs_suse() -> Iterator[VirtualFilesystem]:
yield fs


@pytest.fixture
def fs_linux_sys(fs_linux: VirtualFilesystem) -> Iterator[VirtualFilesystem]:
fs_linux.makedirs("sys")
yield fs_linux


@pytest.fixture
def fs_linux_proc(fs_linux: VirtualFilesystem) -> Iterator[VirtualFilesystem]:
fs = fs_linux
Expand Down
49 changes: 49 additions & 0 deletions tests/plugins/os/unix/linux/test__os.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pathlib
from typing import Iterator

import pytest

from dissect.target import Target
from dissect.target.filesystem import VirtualFilesystem
from dissect.target.plugins.os.unix.linux._os import LinuxPlugin
from tests.conftest import make_os_target


@pytest.fixture
def target_linux_proc_sys(tmp_path: pathlib.Path) -> Iterator[Target]:
root_fs = VirtualFilesystem()
root_fs.makedirs("/proc/sys")
root_fs.makedirs("/sys/module")
target_linux_proc_sys = make_os_target(tmp_path, LinuxPlugin, root_fs)

yield target_linux_proc_sys


@pytest.fixture
def target_linux_windows_folder(tmp_path: pathlib.Path) -> Iterator[Target]:
root_fs = VirtualFilesystem()
root_fs.makedirs("/windows")
root_fs.makedirs("/var")
root_fs.makedirs("/etc")
root_fs.makedirs("/opt")

target_linux_proc_sys = make_os_target(tmp_path, LinuxPlugin, root_fs)

yield target_linux_proc_sys


def test_linux_os(target_linux: Target) -> None:
target_linux.add_plugin(LinuxPlugin)

assert target_linux.os == "linux"


def test_linux_os_windows_folder(target_linux_windows_folder: Target) -> None:
target_linux_windows_folder.add_plugin(LinuxPlugin)
assert target_linux_windows_folder._os_plugin.detect(target_linux_windows_folder) is not None
assert target_linux_windows_folder.os == "linux"


def test_linux_os_proc_sys(target_linux_proc_sys: Target) -> None:
assert target_linux_proc_sys._os_plugin.detect(target_linux_proc_sys) is not None
assert target_linux_proc_sys.os == "linux"
32 changes: 25 additions & 7 deletions tests/plugins/os/windows/test__os.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from typing import Any, Optional
from __future__ import annotations

from typing import Any, Iterator

import pytest

from dissect.target.filesystem import Filesystem
from dissect.target.helpers.regutil import VirtualKey, VirtualValue
from dissect.target.plugins.os.unix.linux._os import LinuxPlugin
from dissect.target.plugins.os.windows._os import WindowsPlugin
from dissect.target.plugins.os.windows.registry import RegistryPlugin
from dissect.target.target import Target
Expand Down Expand Up @@ -31,7 +35,13 @@ def win_plugin(version_target: Target):
return WindowsPlugin(version_target)


def map_version_value(target: Target, name: Optional[str], value: Any):
@pytest.fixture
def target_win_linux_folders(target_win: Filesystem, fs_linux_sys: Filesystem) -> Iterator[Target]:
target_win.fs.mount("/", fs_linux_sys)
yield target_win


def map_version_value(target: Target, name: str | None, value: Any):
if name is not None:
hive = target.registry._root
hive.map_value(CURRENT_VERSION_KEY, name, VirtualValue(hive, name, value))
Expand All @@ -54,7 +64,7 @@ def assert_value(result: Any, value: Any):
def test_windowsplugin__legacy_curre_ntversion(
version_target: Target,
win_plugin: WindowsPlugin,
name: Optional[str],
name: str | None,
value: Any,
):
map_version_value(version_target, name, value)
Expand All @@ -73,7 +83,7 @@ def test_windowsplugin__legacy_curre_ntversion(
def test_windowsplugin__major_version(
version_target: Target,
win_plugin: WindowsPlugin,
name: Optional[str],
name: str | None,
value: Any,
):
map_version_value(version_target, name, value)
Expand All @@ -92,7 +102,7 @@ def test_windowsplugin__major_version(
def test_windowsplugin__minor_version(
version_target: Target,
win_plugin: WindowsPlugin,
name: Optional[str],
name: str | None,
value: Any,
):
map_version_value(version_target, name, value)
Expand Down Expand Up @@ -129,7 +139,7 @@ def test_windowsplugin__nt_version(
version_target: Target,
win_plugin: WindowsPlugin,
keys: list[tuple[str, Any]],
value: Optional[str],
value: str | None,
):
for key_name, key_value in keys:
map_version_value(version_target, key_name, key_value)
Expand Down Expand Up @@ -228,10 +238,18 @@ def test_windowsplugin_version(
version_target: Target,
win_plugin: WindowsPlugin,
keys: list[tuple[str, Any]],
value: Optional[str],
value: str | None,
):
for key_name, key_value in keys:
map_version_value(version_target, key_name, key_value)
result = win_plugin.version

assert_value(result, value)


def test_windows_os_detection_with_linux_folders(target_win_linux_folders: Target) -> None:
fs_linux = LinuxPlugin.detect(target_win_linux_folders)
fs_windows = WindowsPlugin.detect(target_win_linux_folders)

assert fs_linux is None
assert fs_windows is not None

0 comments on commit 38c0145

Please sign in to comment.