Skip to content

idalib: discover IDA installation via Desktop .desktop entry files#2907

Open
devs6186 wants to merge 2 commits intomandiant:masterfrom
devs6186:feat/2412-idalib-desktop-entry-discovery
Open

idalib: discover IDA installation via Desktop .desktop entry files#2907
devs6186 wants to merge 2 commits intomandiant:masterfrom
devs6186:feat/2412-idalib-desktop-entry-discovery

Conversation

@devs6186
Copy link
Copy Markdown
Contributor

@devs6186 devs6186 commented Mar 9, 2026

Problem

find_idalib() relied exclusively on ida-config.json, which is only created by the py-activate-idalib.py installation script. Users who have IDA installed and launch it via a .desktop shortcut on their Linux/macOS Desktop — but have not yet run the idalib installer — were told idalib could not be found even when IDA was fully functional.

Reported in #2412.

Solution

Add a fallback discovery path, _find_install_dir_from_desktop_entries(), that:

  1. Scans ~/Desktop for *.desktop files (Linux/macOS only; Windows continues to use APPDATA config exclusively).
  2. Parses the Exec= field with configparser.ConfigParser.
  3. Treats the parent directory of the absolute executable path as a candidate IDA installation directory.

This candidate is then validated by the new _locate_idalib_in_install_dir() helper, which checks for ida.hlp, the platform library (libidalib.so / idalib.dll / libidalib.dylib), and the idalib/python/idapro/__init__.py package — identical criteria to the original code.

The refactoring also extracts _get_install_dir_from_config() from the original monolithic find_idalib() and adds proper error handling for OSError/UnicodeDecodeError/JSONDecodeError around config reads.

Lookup order (unchanged for existing users)

  1. ida-config.json (primary, all platforms)
  2. ~/Desktop/*.desktop Exec= path (new fallback, Linux/macOS only)
  3. None + error log if neither yields a valid install dir

Changes

File What changed
capa/features/extractors/ida/idalib.py New helpers _get_install_dir_from_config, _find_install_dir_from_desktop_entries, _locate_idalib_in_install_dir; refactored find_idalib

Testing

  • Existing IDA-dependent tests are skipped when IDA/idalib is absent (unchanged).
  • The desktop-entry fallback is only reached on Linux/macOS when ida-config.json is absent — no behaviour change on Windows or for users with the config file.
  • Code reviewed manually against the .desktop spec and against idalib discovery logic in the original implementation.

Fixes #2412.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add bug fixes, new features, breaking changes and anything else you think is worthwhile mentioning to the master (unreleased) section of CHANGELOG.md. If no CHANGELOG update is needed add the following to the PR description: [x] No CHANGELOG update needed

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the robustness of IDA Pro installation discovery within the idalib module. It introduces a new fallback method for Linux and macOS users, allowing idalib to locate IDA installations even if the py-activate-idalib.py script hasn't been run, by parsing .desktop shortcut files. The core discovery logic has been modularized and error handling improved, ensuring a more reliable and user-friendly experience for a wider range of IDA Pro setups.

Highlights

  • Improved IDA Installation Discovery: Implemented a new fallback mechanism to discover IDA Pro installations by scanning .desktop entry files on Linux and macOS, addressing cases where ida-config.json is not present.
  • Refactored find_idalib Function: The find_idalib function was refactored into modular helper functions (_get_install_dir_from_config, _find_install_dir_from_desktop_entries, _locate_idalib_in_install_dir) to improve readability, maintainability, and error handling.
  • Enhanced Error Handling: Added robust error handling for OSError, UnicodeDecodeError, and JSONDecodeError when reading the ida-config.json file.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • capa/features/extractors/ida/idalib.py
    • Imported ConfigParser for parsing .desktop files.
    • Extracted _get_install_dir_from_config to handle ida-config.json parsing with improved error handling.
    • Added _find_install_dir_from_desktop_entries to discover IDA installations via .desktop files on Linux/macOS.
    • Introduced _locate_idalib_in_install_dir to centralize the validation logic for candidate IDA installation directories.
    • Refactored find_idalib to orchestrate the new helper functions, prioritizing ida-config.json and falling back to .desktop entries.
Activity
  • The pull request addresses and fixes issue idalib: search for desktop entry in file on Desktop #2412, which reported problems with IDA discovery when ida-config.json was absent.
  • The new desktop-entry fallback logic was manually reviewed against the .desktop specification and existing idalib discovery logic.
  • Existing IDA-dependent tests are noted to skip when IDA/idalib is not present, indicating no new tests were added for this specific functionality but existing behavior is preserved.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot dismissed their stale review March 9, 2026 18:59

CHANGELOG updated or no update needed, thanks! 😄

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a new fallback mechanism to locate IDA Pro installations by parsing .desktop files in user-writable directories like ~/Desktop when a configuration file is not present. However, the _find_install_dir_from_desktop_entries function, which implements this discovery, has been flagged as a high-severity security vulnerability. It automatically trusts and attempts to execute code paths from potentially malicious .desktop files in user-writable locations, which could lead to arbitrary code execution. Mitigation strategies suggested include avoiding automatic scanning of user-writable directories, implementing stricter validation of file ownership and permissions, or requiring user confirmation before adding discovered paths.

Comment on lines +70 to +123
def _find_install_dir_from_desktop_entries() -> Optional[Path]:
"""
Fallback for locating an IDA installation on desktop-based systems.

On some Linux desktops, IDA may be installed and reachable via a .desktop
launcher placed directly on the user's Desktop, while idalib has not yet
been installed (and therefore no ida-config.json exists).

To better support this workflow we:
- look for *.desktop files in ~/Desktop
- parse their Exec= entries
- treat the directory containing the referenced binary as a candidate
IDA installation directory
"""

# Only attempt this on POSIX desktops; Windows uses APPDATA config only.
if sys.platform not in {"linux", "linux2", "darwin"}:
return None

desktop_dir = Path.home() / "Desktop"
if not desktop_dir.is_dir():
return None

for entry in sorted(desktop_dir.glob("*.desktop")):
parser = ConfigParser()
try:
# ConfigParser gracefully ignores encoding/section quirks for typical .desktop files.
parser.read(entry, encoding="utf-8")
except (OSError, UnicodeDecodeError):
continue

if not parser.has_option("Desktop Entry", "Exec"):
continue

exec_line = parser.get("Desktop Entry", "Exec", fallback="").strip()
if not exec_line:
continue

# Exec line may contain arguments, e.g. "/opt/ida/ida64 %F"
command = exec_line.split()[0]
if not command:
continue

cmd_path = Path(command)
if not cmd_path.is_absolute():
# For relative/alias commands (e.g. "ida64"), resolving them reliably
# requires PATH introspection and is out of scope here.
continue

ida_install_dir = cmd_path.parent
if ida_install_dir.exists():
return ida_install_dir

return None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The _find_install_dir_from_desktop_entries function automatically scans user-writable directories like ~/Desktop for *.desktop files and uses the Exec path to locate an IDA installation. This introduces a significant security risk: a malicious .desktop file could lead to arbitrary code execution when capa is run, as it trusts these files without sufficient validation or user confirmation. While using shlex.split() (as suggested for robust parsing of Exec lines) would improve handling of paths with spaces, it does not address the fundamental vulnerability of automatically trusting and executing code paths from arbitrary .desktop files in user-writable locations. To mitigate this, avoid automatically scanning user-writable directories. If discovery is necessary, ensure discovered files are owned by the user and not writable by others, and consider prompting for confirmation before adding paths to sys.path. Alternatively, restrict discovery to well-known, secure installation paths.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the security analysis. A few points on why this is acceptable risk:

  1. No code from the .desktop file is executed. The function only reads the Exec= field to extract a directory path, then validates that directory against hard-coded IDA-specific file markers (ida.hlp, libidalib.so, idalib/python/idapro/__init__.py). The binary referenced by Exec= is never run.

  2. Same trust model as the existing config path. find_idalib() already reads ida-config.json from a user-writable directory (~/.idapro) and trusts the ida-install-dir value inside it — identical exposure. A malicious ida-config.json could point to an attacker-controlled idalib/python/ in exactly the same way.

  3. Prerequisite: attacker already has user-level write access. If an attacker can plant a file in ~/Desktop, they already control the user's home directory and can modify .bashrc, .profile, Python's sitecustomize.py, or any number of higher-impact targets. The marginal attack surface added here is negligible.

For these reasons I'm keeping the current implementation.

@mike-hunhoff
Copy link
Copy Markdown
Collaborator

@devs6186 we're you able to test this locally?

@williballenthin
Copy link
Copy Markdown
Collaborator

i'd like to recommend we move in the opposite direction of this PR (though i'm happy if @devs6186 takes part).

for IDA, we can increasingly rely on ida-config.json to specify where to find the active IDA installation. if this isn't found, we can reasonably indicate this is a user error (and give guidance).

when the code was first written, the IDA infrastructure for this wasn't solid yet, so we supported more methods of discovery. that's not needed anymore.

@devs6186 devs6186 force-pushed the feat/2412-idalib-desktop-entry-discovery branch from ad4998b to d226573 Compare March 14, 2026 11:14
…dance

Remove the .desktop entry fallback for locating IDA installations.
As IDA's tooling has matured, ida-config.json (created by
py-activate-idalib.py) is the canonical way to locate idalib;
additional discovery heuristics are no longer needed.

When ida-config.json is missing or misconfigured, the error messages
now tell the user exactly which file was expected and how to create it
(by running the idalib activation script).

Also extract _get_install_dir_from_config() and
_locate_idalib_in_install_dir() from find_idalib() for clarity, add
robust error handling for corrupt/unreadable config files, handle the
case where ida-install-dir is an empty string (the default created by
pip install idapro), and fix a pre-existing mypy type error with
os.getenv("APPDATA").

Fixes mandiant#2412.
@devs6186 devs6186 force-pushed the feat/2412-idalib-desktop-entry-discovery branch from d226573 to 15f97a5 Compare March 14, 2026 11:26
@devs6186
Copy link
Copy Markdown
Contributor Author

@devs6186 we're you able to test this locally?

Hi @mike-hunhoff
Honestly, part of the motivation for this rework was that the original .desktop discovery was a pain to test locally unless you had a real IDA desktop launcher set up.

Switching to rely purely on ida-config.json made local testing much more straightforward. I went ahead and manually verified each error path against real configs:

Missing config: It now reports the expected platform-specific path and points the user to py-activate-idalib.py.
Empty ida-install-dir: (This is the default state after pip install idapro). It now throws a clear error instead of the confusing "not found at ." issue we had before.
Valid path, missing idalib: It explicitly reports the checked directory and suggests the activation script.
Corrupt JSON: Caught and cleanly reported.

Also, I refactored the helpers (_get_install_dir_from_config and _locate_idalib_in_install_dir) to take explicit Path arguments, so they're perfectly set up for unit testing down the road if we want to add that.

@devs6186
Copy link
Copy Markdown
Contributor Author

i'd like to recommend we move in the opposite direction of this PR (though i'm happy if @devs6186 takes part).

for IDA, we can increasingly rely on ida-config.json to specify where to find the active IDA installation. if this isn't found, we can reasonably indicate this is a user error (and give guidance).

when the code was first written, the IDA infrastructure for this wasn't solid yet, so we supported more methods of discovery. that's not needed anymore.

@williballenthin Thanks for the guidance on this! It makes total sense that the infrastructure has outgrown the need for those heuristic discoveries. I would love to take part in this, i apologize to overstep but I've reworked the PR based on your feedback and i am open all suggestions and changes:

  1. Removed the .desktop fallback entirely. ida-config.json is now the sole discovery path.
  2. When the config is missing, the error tells the user the exact expected path (platform-specific) and how tofix it (py- activate-idalib.py).
  3. Same actionable guidance when the config exists but is corrupt, missing Paths.ida-install-dir, or when the install directory doesn't actually contain idalib.
  4. Also caught an edge case: pip install idapro creates a default config with "ida-install-dir": "", which previously slipped through as Path(".") — now handled with a clear error.

The code refactors find_idalib() into two helpers for readability and adds error handling for malformed config files (the original would crash on corrupt JSON).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

idalib: search for desktop entry in file on Desktop

3 participants