diff --git a/dissect/target/plugins/os/unix/linux/debian/snap.py b/dissect/target/plugins/os/unix/linux/debian/snap.py index faf307b62..e3ad5dc57 100644 --- a/dissect/target/plugins/os/unix/linux/debian/snap.py +++ b/dissect/target/plugins/os/unix/linux/debian/snap.py @@ -5,7 +5,7 @@ from dissect.target.helpers import configutil from dissect.target.helpers.fsutil import TargetPath from dissect.target.helpers.record import UnixApplicationRecord -from dissect.target.plugin import Plugin, export +from dissect.target.plugin import Plugin, alias, export from dissect.target.target import Target @@ -41,6 +41,7 @@ def _find_installs(self) -> Iterator[TargetPath]: yield path @export(record=UnixApplicationRecord) + @alias("snaps") def snap(self) -> Iterator[UnixApplicationRecord]: """Yield installed snap packages.""" diff --git a/dissect/target/plugins/os/windows/regf/applications.py b/dissect/target/plugins/os/windows/regf/applications.py index 3a1f7873e..413b110d3 100644 --- a/dissect/target/plugins/os/windows/regf/applications.py +++ b/dissect/target/plugins/os/windows/regf/applications.py @@ -32,10 +32,10 @@ def applications(self) -> Iterator[WindowsApplicationRecord]: yield WindowsApplicationRecord( ts_modified=app.ts, ts_installed=values.get("InstallDate"), - name=values.get("DisplayName"), + name=values.get("DisplayName") or app.name, version=values.get("DisplayVersion"), author=values.get("Publisher"), - type="system" if values.get("SystemComponent") else "user", + type="system" if values.get("SystemComponent") or not values else "user", path=values.get("DisplayIcon") or values.get("InstallLocation") or values.get("InstallSource"), _target=self.target, ) diff --git a/tests/_data/plugins/os/unix/applications/code_code.desktop b/tests/_data/plugins/os/unix/applications/code_code.desktop new file mode 100644 index 000000000..78a27aba7 --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/code_code.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:adbb1b293e5cad29e494937d82762b8838244ec55df73917441b958859ece358 +size 972 diff --git a/tests/_data/plugins/os/unix/applications/firefox_firefox.desktop b/tests/_data/plugins/os/unix/applications/firefox_firefox.desktop new file mode 100644 index 000000000..ac5639cba --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/firefox_firefox.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1048d67b7e83577a2fff2239399dcff5c0311685ada454cd7defb800d1f1dc12 +size 9461 diff --git a/tests/_data/plugins/os/unix/applications/gimp.desktop b/tests/_data/plugins/os/unix/applications/gimp.desktop new file mode 100644 index 000000000..6c2813880 --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/gimp.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f77e0c692ed6f090de885e10b9c3f8b94b77fb9a8eb30b95fa019d0b0210e247 +size 15373 diff --git a/tests/_data/plugins/os/unix/applications/python.desktop b/tests/_data/plugins/os/unix/applications/python.desktop new file mode 100644 index 000000000..958029014 --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/python.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc30300f81cb89c4f1d5c3575f539cd4c74293c4752e0d9724f7f359389d31ed +size 224 diff --git a/tests/_data/plugins/os/unix/applications/terminal.desktop b/tests/_data/plugins/os/unix/applications/terminal.desktop new file mode 100644 index 000000000..05750997e --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/terminal.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9caa775a1d5ae964e7c860c22de470a7d2b62edccb8ad1c7cacd88a1b1f3c20 +size 571 diff --git a/tests/_data/plugins/os/unix/applications/vlc.desktop b/tests/_data/plugins/os/unix/applications/vlc.desktop new file mode 100644 index 000000000..66ea7b8ea --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/vlc.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce6aea28e742fb482a755a3e2aa9bc033cbc6136716ddad615454e7ccdd561fa +size 14904 diff --git a/tests/_data/plugins/os/unix/applications/vmware-workstation.desktop b/tests/_data/plugins/os/unix/applications/vmware-workstation.desktop new file mode 100644 index 000000000..6e9eceda2 --- /dev/null +++ b/tests/_data/plugins/os/unix/applications/vmware-workstation.desktop @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05fc84c9998faf664bb08c57e4183604bf1d604d9a2cfaad8332a8a949eba2c5 +size 323 diff --git a/tests/_data/plugins/os/unix/linux/debian/snap/firefox.snap b/tests/_data/plugins/os/unix/linux/debian/snap/firefox.snap new file mode 100644 index 000000000..cfb8c6434 --- /dev/null +++ b/tests/_data/plugins/os/unix/linux/debian/snap/firefox.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2f63e48b7e4a064a993c6a9e82d6e26e1b024192e20017706227e6e054341ac +size 20480 diff --git a/tests/plugins/os/unix/linux/debian/test_snap.py b/tests/plugins/os/unix/linux/debian/test_snap.py index e69de29bb..89c443236 100644 --- a/tests/plugins/os/unix/linux/debian/test_snap.py +++ b/tests/plugins/os/unix/linux/debian/test_snap.py @@ -0,0 +1,36 @@ +from datetime import datetime, timezone +from io import BytesIO + +from dissect.target.filesystem import VirtualFilesystem +from dissect.target.plugins.os.unix._os import UnixPlugin +from dissect.target.plugins.os.unix.linux.debian.snap import SnapPlugin +from dissect.target.target import Target +from tests._utils import absolute_path + + +def test_snap_packages(target_unix_users: Target, fs_unix: VirtualFilesystem) -> None: + """test if snap packages are discovered on unix systems""" + + fs_unix.map_file_fh("/etc/hostname", BytesIO(b"hostname")) + fs_unix.map_file( + "/var/lib/snapd/snaps/firefox_12345.snap", + absolute_path("_data/plugins/os/unix/linux/debian/snap/firefox.snap"), + ) + fs_unix.map_file( + "/var/lib/snapd/snaps/firefox_67890.snap", + absolute_path("_data/plugins/os/unix/linux/debian/snap/firefox.snap"), + ) + + target_unix_users.add_plugin(UnixPlugin) + target_unix_users.add_plugin(SnapPlugin) + + results = list(target_unix_users.snaps()) + assert len(results) == 2 + + assert results[0].hostname == "hostname" + assert results[0].ts_modified == datetime(2024, 9, 17, 13, 18, 58, tzinfo=timezone.utc) + assert results[0].name == "firefox" + assert results[0].version == "129.0.2-1" + assert results[0].author is None + assert results[0].type is None + assert results[0].path == "/var/lib/snapd/snaps/firefox_12345.snap" diff --git a/tests/plugins/os/unix/test_applications.py b/tests/plugins/os/unix/test_applications.py index e69de29bb..ae4153ae3 100644 --- a/tests/plugins/os/unix/test_applications.py +++ b/tests/plugins/os/unix/test_applications.py @@ -0,0 +1,82 @@ +from io import BytesIO + +from dissect.target.filesystem import VirtualFilesystem +from dissect.target.plugins.os.unix._os import UnixPlugin +from dissect.target.plugins.os.unix.applications import UnixApplicationsPlugin +from dissect.target.target import Target +from tests._utils import absolute_path + + +def test_unix_applications_desktop_files(target_unix_users: Target, fs_unix: VirtualFilesystem) -> None: + """test if .desktop files registering installed applications are detected correctly""" + + fs_unix.map_file_fh("/etc/hostname", BytesIO(b"hostname")) + + # system paths + fs_unix.map_file( + "/var/lib/snapd/desktop/applications/firefox_firefox.desktop", + absolute_path("_data/plugins/os/unix/applications/firefox_firefox.desktop"), + ) + fs_unix.map_file( + "/var/lib/snapd/desktop/applications/code_code.desktop", + absolute_path("_data/plugins/os/unix/applications/code_code.desktop"), + ) + fs_unix.map_file( + "/usr/share/applications/gimp.desktop", + absolute_path("_data/plugins/os/unix/applications/gimp.desktop"), + ) + fs_unix.map_file( + "/usr/local/share/applications/vmware-workstation.desktop", + absolute_path("_data/plugins/os/unix/applications/vmware-workstation.desktop"), + ) + fs_unix.map_file( + "/var/lib/flatpak/exports/share/applications/python.desktop", + absolute_path("_data/plugins/os/unix/applications/python.desktop"), + ) + + # user paths + fs_unix.map_file( + "/home/user/.local/share/applications/vlc.desktop", + absolute_path("_data/plugins/os/unix/applications/vlc.desktop"), + ) + fs_unix.map_file( + "/root/.local/share/applications/terminal.desktop", + absolute_path("_data/plugins/os/unix/applications/terminal.desktop"), + ) + + target_unix_users.add_plugin(UnixPlugin) + target_unix_users.add_plugin(UnixApplicationsPlugin) + results = sorted(list(target_unix_users.applications()), key=lambda r: r.name) + + assert len(results) == 7 + + assert results[0].ts_installed is None + assert results[0].name == "Firefox Web Browser" + assert results[0].version == "1.0" + assert results[0].author is None + assert results[0].type == "user" + assert ( + results[0].path + == "env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/firefox_firefox.desktop /snap/bin/firefox %u" + ) # noqa: E501 + assert results[0].hostname == "hostname" + + assert [r.name for r in results] == [ + "Firefox Web Browser", + "GNU Image Manipulation Program", + "Python (v3.12)", + "Terminal", + "VLC media player", + "VMware Workstation", + "Visual Studio Code", + ] + + assert [r.path for r in results] == [ + "env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/firefox_firefox.desktop /snap/bin/firefox %u", + "gimp-2.10 %U", + "/usr/bin/python3.12", + "gnome-terminal", + "/usr/bin/vlc --started-from-file %U", + "/usr/bin/vmware %U", + "env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/code_code.desktop /snap/bin/code --force-user-env %F", + ] diff --git a/tests/plugins/os/windows/regf/test_applications.py b/tests/plugins/os/windows/regf/test_applications.py new file mode 100644 index 000000000..fcb5afe8a --- /dev/null +++ b/tests/plugins/os/windows/regf/test_applications.py @@ -0,0 +1,66 @@ +from datetime import datetime, timezone + +from dissect.target.helpers.regutil import VirtualHive, VirtualKey +from dissect.target.plugins.os.windows.regf.applications import ( + WindowsApplicationsPlugin, +) +from dissect.target.target import Target + + +def test_windows_applications(target_win_users: Target, hive_hklm: VirtualHive) -> None: + """test if windows applications are detected correctly in the registry""" + + firefox_name = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mozilla Firefox 123.0.1 (x64 nl)" + firefox_key = VirtualKey(hive_hklm, firefox_name) + firefox_key.add_value("Comments", "Mozilla Firefox 123.0.1 (x64 nl)") + firefox_key.add_value("DisplayIcon", "C:\\Program Files\\Mozilla Firefox\\firefox.exe,0") + firefox_key.add_value("DisplayName", "Mozilla Firefox (x64 nl)") + firefox_key.add_value("DisplayVersion", "123.0.1") + firefox_key.add_value("EstimatedSize", 238271) + firefox_key.add_value("HelpLink", "https://support.mozilla.org") + firefox_key.add_value("InstallLocation", "C:\\Program Files\\Mozilla Firefox") + firefox_key.add_value("NoModify", 1) + firefox_key.add_value("NoRepair", 1) + firefox_key.add_value("Publisher", "Mozilla") + firefox_key.add_value("URLInfoAbout", "https://www.mozilla.org") + firefox_key.add_value("URLUpdateInfo", "https://www.mozilla.org/firefox/123.0.1/releasenotes") + firefox_key.add_value("UninstallString", '"C:\\Program Files\\Mozilla Firefox\\uninstall\\helper.exe"') + hive_hklm.map_key(firefox_name, firefox_key) + + chrome_name = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{47FB91DD-98F3-3C87-A963-357B14EAC7C9}" + chrome_key = VirtualKey(hive_hklm, chrome_name) + chrome_key.add_value("DisplayVersion", "122.0.6261.95") + chrome_key.add_value("InstallDate", "20240301") + chrome_key.add_value("InstallLocation", "") + chrome_key.add_value("InstallSource", "C:\\Users\\user\\Desktop\\GoogleChromeEnterpriseBundle64\\Installers\\") + chrome_key.add_value("ModifyPath", "MsiExec.exe /X{47FB91DD-98F3-3C87-A963-357B14EAC7C9}") + chrome_key.add_value("NoModify", 1) + chrome_key.add_value("Publisher", "Google LLC") + chrome_key.add_value("EstimatedSize", 113725) + chrome_key.add_value("UninstallString", "MsiExec.exe /X{47FB91DD-98F3-3C87-A963-357B14EAC7C9}") + chrome_key.add_value("VersionMajor", 70) + chrome_key.add_value("VersionMinor", 29) + chrome_key.add_value("WindowsInstaller", 1) + chrome_key.add_value("Version", 1176322143) + chrome_key.add_value("Language", 1033) + chrome_key.add_value("DisplayName", "Google Chrome") + hive_hklm.map_key(chrome_name, chrome_key) + + target_win_users.add_plugin(WindowsApplicationsPlugin) + results = sorted(list(target_win_users.applications()), key=lambda r: r.name) + + assert len(results) == 2 + + assert results[0].ts_installed == datetime(2024, 3, 1, 0, 0, 0, tzinfo=timezone.utc) + assert results[0].name == "Google Chrome" + assert results[0].version == "122.0.6261.95" + assert results[0].author == "Google LLC" + assert results[0].type == "user" + assert results[0].path == "C:\\Users\\user\\Desktop\\GoogleChromeEnterpriseBundle64\\Installers\\" + + assert results[0].ts_installed is None + assert results[0].name == "Mozilla Firefox (x64 nl)" + assert results[0].version == "123.0.1" + assert results[0].author == "Mozilla" + assert results[0].type == "user" + assert results[0].path == "C:\\Program Files\\Mozilla Firefox\\firefox.exe,0" diff --git a/tests/plugins/os/windows/test_applications.py b/tests/plugins/os/windows/test_applications.py deleted file mode 100644 index e69de29bb..000000000