Skip to content

TEST#6

Open
Radiump123 wants to merge 17 commits into
testing-stufffrom
dev
Open

TEST#6
Radiump123 wants to merge 17 commits into
testing-stufffrom
dev

Conversation

@Radiump123
Copy link
Copy Markdown
Owner

@Radiump123 Radiump123 commented May 14, 2026

Summary by Sourcery

Refactor low-level device, filesystem, and ISO handling to use pure Python helpers instead of external utilities, improve UX around flashing and bad block checks, persist and auto-detect language and theme, and update project metadata and links to the new GitHub organization.

New Features:

  • Add pure Python block device utilities for mounting, partitioning, wiping, and imaging, and switch flashing and formatting flows to use them.
  • Introduce a pure Python ISO-9660 reader for Windows/Linux ISO detection and marker scanning.
  • Add a helper to open URLs as the non-root user when running elevated, and use it throughout the GUI for browser links.
  • Automatically detect and persist the application language using system locale and config files, and wire this into startup.

Bug Fixes:

  • Ensure USB image writes and failures return a boolean result instead of propagating subprocess errors.
  • Fix NVMe device handling so flash operations target the correct base device path.
  • Handle sysfs/metadata lookup failures in USB info and detection helpers without crashing, falling back to sensible defaults.
  • Correct bad-block checking so it is controlled by a boolean flag with an explicit pass count, and ensure GUI state and worker logic remain consistent.
  • Reset hash verification progress to 100% on completion to avoid a stuck 99% indicator.

Enhancements:

  • Replace various external tools (dd, mount/umount, sfdisk, wipefs, lsblk, 7z/isoinfo, mkdir/rm) with internal Python implementations to reduce dependencies and improve portability.
  • Improve process and terminal cleanup when cancelling operations by scanning /proc for device users, killing them, and resetting the TTY via termios.
  • Add a Rufus-style destructive flash confirmation dialog and wire bad-block pass count through to the flash options.
  • Tighten polkit agent detection using in-process /proc scanning instead of invoking pgrep.
  • Refine dialogs and buttons in the GUI (log window close button, resized About dialog, localized button labels) for better usability.

Build:

  • Add a build script that creates a standalone PyInstaller binary and bundles required external tools.
  • Update pyproject and Briefcase metadata to point to the new GitHub organization, bundle ID, and author.

Documentation:

  • Update README and badges to reference the new GitHub repository location.

Tests:

  • Adjust and extend tests to cover new block device and ISO helpers, changed bad-block and partitioning logic, URL opening in elevated/root scenarios, and sysfs-based USB detection semantics.

R. Larocque added 9 commits May 13, 2026 12:49
…re missing, replaced mentions of the old GitHub by the new repo, fixed the issue Hogjects#200.

No AI used.
 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is up to date with 'origin/dev'.

 Changes to be committed:
	modified:   src/lufus/gui/dialogs.py
	modified:   src/lufus/gui/gui.py
	modified:   src/lufus/gui/languages/Deutsch.csv
	modified:   src/lufus/gui/languages/English.csv
	modified:   "src/lufus/gui/languages/Espa\303\261ol.csv"
	modified:   "src/lufus/gui/languages/Fran\303\247ais.csv"
	modified:   "src/lufus/gui/languages/Portugue\314\202s Brasileiro.csv"
	modified:   src/lufus/gui/languages/Svenska.csv
	modified:   "src/lufus/gui/languages/\320\240\321\203\321\201\321\201\320\272\320\270\320\271.csv"
	modified:   "src/lufus/gui/languages/\321\203\320\272\321\200\320\260\321\227\320\275\321\201\321\214\320\272\320\260.csv"
	modified:   "src/lufus/gui/languages/\330\271\330\261\330\250\331\212.csv"
	modified:   "src/lufus/gui/languages/\340\246\254\340\246\276\340\246\202\340\246\262\340\246\276.csv"
 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is ahead of 'origin/dev' by 1 commit.
   (use "git push" to publish your local commits)

 Changes to be committed:
	modified:   src/lufus/gui/dialogs.py
	modified:   src/lufus/gui/gui.py
	modified:   "src/lufus/gui/languages/Espa\303\261ol.csv"
	modified:   "src/lufus/gui/languages/\320\240\321\203\321\201\321\201\320\272\320\270\320\271.csv"
	modified:   "src/lufus/gui/languages/\321\203\320\272\321\200\320\260\321\227\320\275\321\201\321\214\320\272\320\260.csv"
	modified:   "src/lufus/gui/languages/\330\271\330\261\330\250\331\212.csv"
	modified:   "src/lufus/gui/languages/\340\246\254\340\246\276\340\246\202\340\246\262\340\246\276.csv"
…d some scaling.

 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is ahead of 'origin/dev' by 2 commits.
   (use "git push" to publish your local commits)

 Changes to be committed:
	new file:   src/lufus/browse_freely.py
	modified:   src/lufus/gui/dialogs.py
	modified:   src/lufus/gui/gui.py
 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is ahead of 'origin/dev' by 4 commits.
   (use "git push" to publish your local commits)

 Changes to be committed:
	new file:   tests/test_browse_freely.py
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 14, 2026

Reviewer's Guide

Refactors low-level disk, ISO, and browser handling into pure-Python helpers while adding safer flashing UX (pre-flash warning, bad-block passes), persistent language/theme handling across privilege elevation, and updating all GitHub/org metadata and tests to match the new Hogjects organization.

Sequence diagram for updated flashing flow with warning and bad-block check

sequenceDiagram
    actor User
    participant MainWindow as gui.MainWindow
    participant FlashWorker as gui.workers.FlashWorker
    participant Formatting as drives.formatting
    participant FlashUSB as writing.flash_usb
    participant BlockOps as block_ops

    User->>MainWindow: click btn_start
    MainWindow->>MainWindow: perform_flash()
    MainWindow->>MainWindow: QMessageBox
    alt user cancels
        MainWindow->>MainWindow: log_message("Flash aborted by user...")
        MainWindow->>User: re-enable Start, reset progress
    else user confirms
        MainWindow->>FlashWorker: start()
        FlashWorker->>FlashWorker: run()
        FlashWorker->>Formatting: check_device_bad_blocks(passes=states.bad_blocks_passes)
        alt badblocks fails
            FlashWorker->>MainWindow: flash_done(False)
        else badblocks OK or disabled
            FlashWorker->>FlashUSB: flash_image(device_node, iso_path,...)
            FlashUSB->>BlockOps: write_device_image(iso_path, device, bs, progress_cb, status_cb)
            BlockOps-->>FlashUSB: return code
            FlashUSB-->>FlashWorker: success / failure
            FlashWorker-->>MainWindow: flash_done(result)
        end
    end
Loading

File-Level Changes

Change Details Files
Introduce shared pure-Python block/ISO helpers and migrate flashing, formatting, USB info, and Windows detection code away from ad-hoc subprocess calls.
  • Add lufus.block_ops with sysfs-based size/label probing, ctypes-based mount/umount, GPT/MBR writers, superblock wiping, and generic image writer with progress callbacks.
  • Replace dd-based flashing in flash_usb with write_device_image, returning error codes instead of raising and updating tests to assert correct device stripping and failure handling.
  • Refactor Windows USB flashing to use block_ops for wipefs, partitioning, mounting, sync, and UEFI-NTFS writes, and update tests accordingly.
  • Switch badblocks and geometry helpers in formatting to use block_ops for block size and mount/umount, adjust unmount/remount semantics, and simplify partition scheme application to use write_single_partition_table.
  • Replace lsblk-based size/label discovery in get_usb_info and find_usb with block_ops helpers and adjust tests to mock new APIs.
  • Introduce lufus.iso9660 pure-Python ISO directory listing and markers lookup, and rework Windows/Linux ISO detection to use it instead of 7z/isoinfo with corresponding test updates.
src/lufus/block_ops.py
src/lufus/iso9660.py
src/lufus/writing/flash_usb.py
src/lufus/writing/windows/flash.py
src/lufus/drives/formatting.py
src/lufus/drives/get_usb_info.py
src/lufus/drives/find_usb.py
src/lufus/writing/windows/detect.py
tests/test_flash_usb_and_find_usb_fixes.py
tests/test_formatting.py
tests/test_get_usb_info.py
tests/test_find_usb.py
tests/test_get_usb_info_and_detect_windows_fixes.py
tests/test_linux_iso_detection.py
Improve flashing safety and device cleanup, including pre-flash confirmation, process killing, terminal reset, and optional bad-blocks checks with pass count.
  • Add a Rufus-style destructive flash confirmation dialog before starting flashing, including device name, and bail out cleanly when cancelled.
  • Introduce helpers in gui.py to find and kill processes using a device via /proc instead of lsof/fuser, and log the results during cancel_process.
  • Replace stty sane with a termios-based _reset_terminal helper for terminal sessions.
  • Change bad-blocks configuration from an int to a bool plus bad_blocks_passes, propagate these into perform_flash options, and implement optional bad-blocks checks in FlashWorker before formatting/flashing.
  • Adjust progress bar formatting after verification and when aborting flashing, ensuring it is cleared/reset appropriately.
src/lufus/gui/gui.py
src/lufus/drives/formatting.py
src/lufus/gui/workers.py
src/lufus/state.py
Persist and auto-detect UI language, and ensure language/theme settings survive privilege elevation.
  • Add detect_system_language in gui.i18n using locale/env mapping to friendly language names, with a fallback to English.
  • Add _load_initial_language in start_gui to load LUFUS_LANGUAGE from env, fall back to persisted active_language or detected system language, and call it both pre- and post-elevation.
  • Persist selected language in apply_language to a config file under user_config_dir("Lufus")/active_language, handling errors via log_message.
  • Update elevate_privileges to propagate LUFUS_LANGUAGE alongside LUFUS_THEME and other environment variables.
  • Tighten initial theme loading to prefer env theme and avoid reloading the default theme from disk unnecessarily.
src/lufus/gui/i18n.py
src/lufus/gui/start_gui.py
src/lufus/gui/gui.py
src/lufus/utils.py
src/lufus/state.py
tests/test_states_and_autodetect_fixes.py
Centralize safe URL opening under non-root users and integrate it into GUI and update flows.
  • Add lufus.browse_freely.open_url_non_root which attempts to open URLs via runuser/xdg-open as the original user when running as root (using PKEXEC_UID/SUDO_USER), falling back to webbrowser.open.
  • Replace inline pkexec/runuser logic in gui._open_url, About dialog buttons, and update check dialogs with open_url_non_root, updating GitHub URLs to the new Hogjects org.
  • Add unit tests covering regular-user, pkexec-root, and sudo-root scenarios for open_url_non_root.
src/lufus/browse_freely.py
src/lufus/gui/gui.py
src/lufus/gui/dialogs.py
tests/test_browse_freely.py
Update USB info, Ventoy install, and Windows tweaks flows to use new helpers and be more robust.
  • Adjust get_usb_info to use get_device_size/get_device_label, keep returning info even when size/label probing fails, and update tests to reflect sysfs-based behavior and label fallbacks.
  • Refactor install_ventoy.install_grub to use block_ops.wipe_superblock/write_gpt/reread_partitions/block_mount/block_umount instead of sfdisk/partprobe/mount/umount, with safer temp dirs and cleanup.
  • In Windows tweaks, replace shell mkdir/rm calls with os.makedirs and shutil.rmtree for boot.wim registry hacks.
src/lufus/drives/get_usb_info.py
src/lufus/writing/install_ventoy.py
src/lufus/writing/windows/tweaks.py
tests/test_get_usb_info.py
tests/test_get_usb_info_and_detect_windows_fixes.py
Polish GUI dialogs and labels for better UX and localization consistency.
  • Extend LogWindow with an explicit Close button and tweak About window sizing and font to fit content better.
  • Update About dialog buttons to use non-localized Discord/GitHub texts, ensure minimum widths, and route URL opens through open_url_non_root with updated Hogjects URLs.
  • Localize WinTweaks dialog buttons to use OK/Cancel translation keys.
  • Adjust bad-blocks checkbox handling to set a bool and pass count rather than magic ints.
src/lufus/gui/dialogs.py
src/lufus/gui/gui.py
src/lufus/gui/languages/*.csv
Modernize metadata, build, and documentation to align with the Hogjects organization and add a PyInstaller build script.
  • Update pyproject.toml URLs, bundle ID, and author to use the Hogjects GitHub org, and adjust README license badge URL accordingly.
  • Change various hardcoded GitHub owner strings in the GUI and update-check logic from Hog185 to Hogjects.
  • Add scripts/build-standalone.sh to build a one-file PyInstaller binary, bundling necessary system tools and GUI assets.
pyproject.toml
README.md
src/lufus/gui/gui.py
src/lufus/gui/dialogs.py
scripts/build-standalone.sh

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In launch_gui_with_usb_data, _load_initial_language() is called both before and after elevate_privileges(), which means the post-elevation call may read the root user’s config and overwrite the intended user language; consider only initializing language in the non-root context or guarding the second call appropriately.
  • In open_url_non_root, when only SUDO_USER is set you never derive a UID, so XDG_RUNTIME_DIR is left empty; consider resolving the UID for sudo_user (e.g. via pwd.getpwnam) to populate a more accurate environment for GUI apps.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `launch_gui_with_usb_data`, `_load_initial_language()` is called both before and after `elevate_privileges()`, which means the post-elevation call may read the root user’s config and overwrite the intended user language; consider only initializing language in the non-root context or guarding the second call appropriately.
- In `open_url_non_root`, when only `SUDO_USER` is set you never derive a UID, so `XDG_RUNTIME_DIR` is left empty; consider resolving the UID for `sudo_user` (e.g. via `pwd.getpwnam`) to populate a more accurate environment for GUI apps.

## Individual Comments

### Comment 1
<location path="src/lufus/browse_freely.py" line_range="39" />
<code_context>
+
+                # Method 1: Use runuser which is specifically designed for this
+                # We need to preserve the environment for the GUI session
+                env = {
+                    "DISPLAY": os.environ.get("DISPLAY", ":0"),
+                    "XAUTHORITY": os.environ.get("XAUTHORITY", ""),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Environment for the spawned browser process does not set HOME, which may cause profile or permission issues.

Since many GUI apps depend on `HOME` for profiles and config, relying only on `DISPLAY`, `XAUTHORITY`, `XDG_RUNTIME_DIR`, `WAYLAND_DISPLAY`, and `PATH` risks using root’s home (or none) when running as another user, especially in the `sudo_user` case without `target_uid`. Please also set `HOME` to the target user’s home directory (e.g. `pwd.getpwnam(target_user).pw_dir`) and add it to `env` when available.

Suggested implementation:

```python
import os
import pwd

```

```python
        if target_user:
            try:
                log.info(f"Attempting to open URL as user {target_user} (UID {target_uid}): {url}")

                # Determine the target user's home directory so GUI apps use the correct profile/config
                home_dir = None
                try:
                    home_dir = pwd.getpwnam(target_user).pw_dir
                except KeyError:
                    log.warning("Could not determine home directory for user %s", target_user)

                # Method 1: Use runuser which is specifically designed for this
                # We need to preserve the environment for the GUI session
                env = {
                    "DISPLAY": os.environ.get("DISPLAY", ":0"),
                    "XAUTHORITY": os.environ.get("XAUTHORITY", ""),
                    "XDG_RUNTIME_DIR": os.environ.get(
                        "XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
                    ),
                    "WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
                    "PATH": "/usr/local/bin:/usr/bin:/bin",
                    # Ensure HOME is set so GUI apps use the target user's profile and config
                    "HOME": home_dir or os.environ.get("HOME", ""),
                }

                # Filter out empty values
                env = {k: v for k, v in env.items() if v}

```

If `pwd` is already imported elsewhere in this file, adjust the import edit accordingly to avoid duplicate imports (e.g. by adding it to an existing import line instead of introducing a new one).
</issue_to_address>

### Comment 2
<location path="tests/test_browse_freely.py" line_range="11-20" />
<code_context>
+        self.assertEqual(env.get("DISPLAY"), ":0")
+        self.assertEqual(env.get("XDG_RUNTIME_DIR"), "/run/user/1000")
+
+    @patch("os.geteuid")
+    @patch("os.environ.get")
+    @patch("subprocess.Popen")
+    def test_open_url_as_root_via_sudo(self, mock_popen, mock_env_get, mock_geteuid):
+        # Simulate root (UID 0)
+        mock_geteuid.return_value = 0
</code_context>
<issue_to_address>
**suggestion (testing):** Consider using `patch.dict(os.environ, ...)` instead of patching `os.environ.get` to make the tests more realistic and resilient.

Patching `os.environ.get` with a `side_effect` intercepts every env lookup (e.g. `DISPLAY`, `XAUTHORITY`, `XDG_RUNTIME_DIR`), which couples the tests to the exact way `open_url_non_root` reads the environment. Instead, patching `os.environ` via `patch.dict(os.environ, {"PKEXEC_UID": "1000", ...}, clear=False)` (and similarly for the sudo case) more accurately reflects real usage and keeps the tests resilient to internal changes in environment access.

Suggested implementation:

```python
    @patch("os.geteuid")
    @patch("subprocess.Popen")
    @patch("pwd.getpwuid")
    def test_open_url_as_root_via_pkexec(self, mock_getpwuid, mock_popen, mock_geteuid):

```

```python
        # Mock environment variables
        env_overrides = {
            "PKEXEC_UID": "1000",
            "DISPLAY": ":0",
            "XDG_RUNTIME_DIR": "/run/user/1000",
        }

        with patch.dict(os.environ, env_overrides, clear=False):
            open_url_non_root(self.url)

        # Mock pwd info

```

1. Apply a similar refactor to the `test_open_url_as_root_via_sudo` test: remove any `@patch("os.environ.get")` decorator and associated `mock_env_get` parameter, and replace any `side_effect`-based environment mocking with a `with patch.dict(os.environ, {...}, clear=False):` block that sets the sudo-related variables your production code expects (e.g. `SUDO_USER`, `DISPLAY`, `XDG_RUNTIME_DIR`, etc.), wrapping the `open_url_non_root(self.url)` call in that context.
2. If there are other tests in this file that also patch `os.environ.get`, consider converting them to `patch.dict` in the same way to keep the test suite consistent and resilient to internal environment access changes.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/lufus/browse_freely.py
Comment on lines +11 to +20
@patch("os.geteuid")
@patch("webbrowser.open")
def test_open_url_non_root_as_regular_user(self, mock_webbrowser, mock_geteuid):
# Simulate regular user (UID 1000)
mock_geteuid.return_value = 1000

open_url_non_root(self.url)

# Should fallback to standard webbrowser.open
mock_webbrowser.assert_called_once_with(self.url)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Consider using patch.dict(os.environ, ...) instead of patching os.environ.get to make the tests more realistic and resilient.

Patching os.environ.get with a side_effect intercepts every env lookup (e.g. DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR), which couples the tests to the exact way open_url_non_root reads the environment. Instead, patching os.environ via patch.dict(os.environ, {"PKEXEC_UID": "1000", ...}, clear=False) (and similarly for the sudo case) more accurately reflects real usage and keeps the tests resilient to internal changes in environment access.

Suggested implementation:

    @patch("os.geteuid")
    @patch("subprocess.Popen")
    @patch("pwd.getpwuid")
    def test_open_url_as_root_via_pkexec(self, mock_getpwuid, mock_popen, mock_geteuid):
        # Mock environment variables
        env_overrides = {
            "PKEXEC_UID": "1000",
            "DISPLAY": ":0",
            "XDG_RUNTIME_DIR": "/run/user/1000",
        }

        with patch.dict(os.environ, env_overrides, clear=False):
            open_url_non_root(self.url)

        # Mock pwd info
  1. Apply a similar refactor to the test_open_url_as_root_via_sudo test: remove any @patch("os.environ.get") decorator and associated mock_env_get parameter, and replace any side_effect-based environment mocking with a with patch.dict(os.environ, {...}, clear=False): block that sets the sudo-related variables your production code expects (e.g. SUDO_USER, DISPLAY, XDG_RUNTIME_DIR, etc.), wrapping the open_url_non_root(self.url) call in that context.
  2. If there are other tests in this file that also patch os.environ.get, consider converting them to patch.dict in the same way to keep the test suite consistent and resilient to internal environment access changes.

@Radiump123
Copy link
Copy Markdown
Owner Author

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/lufus/gui/i18n.py" line_range="29-38" />
<code_context>
+    Falls back to English if detection fails or no match is found.
+    """
+    try:
+        loc = locale.getdefaultlocale()
+    except Exception:
+        loc = (None, None)
</code_context>
<issue_to_address>
**suggestion:** Using `locale.getdefaultlocale()` may be problematic going forward due to deprecation; consider an alternative approach.

`locale.getdefaultlocale()` is deprecated in recent Python versions and may be removed. Since only the language is needed, consider alternatives such as `locale.getlocale()[0]`, `os.environ.get("LANG")`, or `locale.setlocale(locale.LC_CTYPE, "")` followed by `locale.getlocale()` to avoid relying on a deprecated API.

```suggestion
def detect_system_language() -> str:
    """Detect the system locale and return the matching language name.

    Falls back to English if detection fails or no match is found.
    """
    try:
        # Ensure locale is initialized from the environment, then read current locale.
        locale.setlocale(locale.LC_CTYPE, "")
        loc = locale.getlocale()
    except Exception:
        loc = (None, None)

    lang_code = (loc[0] or "") or os.environ.get("LANG", "")
```
</issue_to_address>

### Comment 2
<location path="tests/test_flash_usb_and_find_usb_fixes.py" line_range="148-149" />
<code_context>
                 pass

         class FakePipe:
-            def readline(self):
+            def read(self, n=-1):
                 return b""

</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the fake stderr pipe to assert that `read` is called with the expected chunk size (4096).

`FakePipe.read` currently ignores `n` and always returns `b""`, so the test doesn’t verify that `process.stderr.read(4096)` is actually called with the intended chunk size. Consider recording the passed-in `n` (e.g., `self.last_n = n`) and asserting it is `4096` (or at least `> 0`) after the call to catch regressions like switching back to `readline()` or changing the chunk size.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/lufus/gui/i18n.py Outdated
Comment on lines +148 to +149
class FakePipe:
def readline(self):
def read(self, n=-1):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Strengthen the fake stderr pipe to assert that read is called with the expected chunk size (4096).

FakePipe.read currently ignores n and always returns b"", so the test doesn’t verify that process.stderr.read(4096) is actually called with the intended chunk size. Consider recording the passed-in n (e.g., self.last_n = n) and asserting it is 4096 (or at least > 0) after the call to catch regressions like switching back to readline() or changing the chunk size.

@Radiump123
Copy link
Copy Markdown
Owner Author

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/lufus/gui/i18n.py" line_range="34-41" />
<code_context>
+    Falls back to English if detection fails or no match is found.
+    """
+    try:
+        # Ensure locale is initialized from the environment, then read current locale.
+        locale.setlocale(locale.LC_CTYPE, "")
+        loc = locale.getlocale()
+    except Exception:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Calling `locale.setlocale` here mutates global process state and may have unintended side effects.

`locale.setlocale(locale.LC_CTYPE, "")` here mutates the global locale and can unexpectedly impact formatting/parsing and libraries that depend on locale (e.g. Qt). If you only need to detect the system locale, prefer `locale.getdefaultlocale()` or environment variables (e.g. `$LANG`) without changing the active locale, or temporarily change it and immediately restore the original value.

```suggestion
    try:
        # Read the system locale without mutating global locale state.
        loc = locale.getdefaultlocale()
    except Exception:
        loc = (None, None)

    # Fallback to the process locale if the default locale could not be determined.
    if not loc or not loc[0]:
        try:
            loc = locale.getlocale()
        except Exception:
            loc = (None, None)

    lang_code = (loc[0] or "") or os.environ.get("LANG", "")
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/lufus/gui/i18n.py
@Radiump123
Copy link
Copy Markdown
Owner Author

broke smth i think

@Radiump123
Copy link
Copy Markdown
Owner Author

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • open_url_non_root() unconditionally calls os.geteuid(), which will raise on platforms without that API (e.g. Windows); consider guarding this with hasattr/os.name checks or a try/except and treating such platforms as non-root so the function safely falls back to webbrowser.open.
  • _load_initial_language() swallows all exceptions and may leave state.language unset or stale; it would be safer to explicitly default to 'English' (or another known language) when detection/persistence fails so the GUI always has a predictable language value.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- open_url_non_root() unconditionally calls os.geteuid(), which will raise on platforms without that API (e.g. Windows); consider guarding this with hasattr/os.name checks or a try/except and treating such platforms as non-root so the function safely falls back to webbrowser.open.
- _load_initial_language() swallows all exceptions and may leave state.language unset or stale; it would be safer to explicitly default to 'English' (or another known language) when detection/persistence fails so the GUI always has a predictable language value.

## Individual Comments

### Comment 1
<location path="src/lufus/browse_freely.py" line_range="43-55" />
<code_context>
+
+                # Method 1: Use runuser which is specifically designed for this
+                # We need to preserve the environment for the GUI session
+                env = {
+                    "DISPLAY": os.environ.get("DISPLAY", ":0"),
+                    "XAUTHORITY": os.environ.get("XAUTHORITY", ""),
+                    "XDG_RUNTIME_DIR": os.environ.get(
+                        "XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
+                    ),
+                    "WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
+                    "PATH": "/usr/local/bin:/usr/bin:/bin",
+                    # Ensure HOME is set so GUI apps use the target user's profile and config
+                    "HOME": home_dir or os.environ.get("HOME", ""),
+                }
+
</code_context>
<issue_to_address>
**suggestion:** Environment for `runuser` may be missing session-related variables needed by some desktop environments.

For `xdg-open` via `runuser`, some DEs (especially under Wayland) also depend on `DBUS_SESSION_BUS_ADDRESS` and similar session vars. Consider propagating it when present, for example:

```python
env = {
    "DISPLAY": os.environ.get("DISPLAY", ":0"),
    "XAUTHORITY": os.environ.get("XAUTHORITY", ""),
    "XDG_RUNTIME_DIR": os.environ.get(
        "XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
    ),
    "WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
    "DBUS_SESSION_BUS_ADDRESS": os.environ.get("DBUS_SESSION_BUS_ADDRESS", ""),
    "PATH": "/usr/local/bin:/usr/bin:/bin",
    "HOME": home_dir or os.environ.get("HOME", ""),
}
```
This reduces cases where `xdg-open` fails under certain desktop environments.

```suggestion
                # Method 1: Use runuser which is specifically designed for this
                # We need to preserve the environment for the GUI session
                env = {
                    "DISPLAY": os.environ.get("DISPLAY", ":0"),
                    "XAUTHORITY": os.environ.get("XAUTHORITY", ""),
                    "XDG_RUNTIME_DIR": os.environ.get(
                        "XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
                    ),
                    "WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
                    # Propagate DBUS session bus so xdg-open works under more DEs/Wayland sessions
                    "DBUS_SESSION_BUS_ADDRESS": os.environ.get("DBUS_SESSION_BUS_ADDRESS", ""),
                    "PATH": "/usr/local/bin:/usr/bin:/bin",
                    # Ensure HOME is set so GUI apps use the target user's profile and config
                    "HOME": home_dir or os.environ.get("HOME", ""),
                }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +43 to +55
# Method 1: Use runuser which is specifically designed for this
# We need to preserve the environment for the GUI session
env = {
"DISPLAY": os.environ.get("DISPLAY", ":0"),
"XAUTHORITY": os.environ.get("XAUTHORITY", ""),
"XDG_RUNTIME_DIR": os.environ.get(
"XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
),
"WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
"PATH": "/usr/local/bin:/usr/bin:/bin",
# Ensure HOME is set so GUI apps use the target user's profile and config
"HOME": home_dir or os.environ.get("HOME", ""),
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Environment for runuser may be missing session-related variables needed by some desktop environments.

For xdg-open via runuser, some DEs (especially under Wayland) also depend on DBUS_SESSION_BUS_ADDRESS and similar session vars. Consider propagating it when present, for example:

env = {
    "DISPLAY": os.environ.get("DISPLAY", ":0"),
    "XAUTHORITY": os.environ.get("XAUTHORITY", ""),
    "XDG_RUNTIME_DIR": os.environ.get(
        "XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
    ),
    "WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
    "DBUS_SESSION_BUS_ADDRESS": os.environ.get("DBUS_SESSION_BUS_ADDRESS", ""),
    "PATH": "/usr/local/bin:/usr/bin:/bin",
    "HOME": home_dir or os.environ.get("HOME", ""),
}

This reduces cases where xdg-open fails under certain desktop environments.

Suggested change
# Method 1: Use runuser which is specifically designed for this
# We need to preserve the environment for the GUI session
env = {
"DISPLAY": os.environ.get("DISPLAY", ":0"),
"XAUTHORITY": os.environ.get("XAUTHORITY", ""),
"XDG_RUNTIME_DIR": os.environ.get(
"XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
),
"WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
"PATH": "/usr/local/bin:/usr/bin:/bin",
# Ensure HOME is set so GUI apps use the target user's profile and config
"HOME": home_dir or os.environ.get("HOME", ""),
}
# Method 1: Use runuser which is specifically designed for this
# We need to preserve the environment for the GUI session
env = {
"DISPLAY": os.environ.get("DISPLAY", ":0"),
"XAUTHORITY": os.environ.get("XAUTHORITY", ""),
"XDG_RUNTIME_DIR": os.environ.get(
"XDG_RUNTIME_DIR", f"/run/user/{target_uid}" if target_uid else ""
),
"WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
# Propagate DBUS session bus so xdg-open works under more DEs/Wayland sessions
"DBUS_SESSION_BUS_ADDRESS": os.environ.get("DBUS_SESSION_BUS_ADDRESS", ""),
"PATH": "/usr/local/bin:/usr/bin:/bin",
# Ensure HOME is set so GUI apps use the target user's profile and config
"HOME": home_dir or os.environ.get("HOME", ""),
}

@Radiump123
Copy link
Copy Markdown
Owner Author

Dgaf about windows lmao

@Radiump123
Copy link
Copy Markdown
Owner Author

and Hogjects#184, too

 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is up to date with 'origin/dev'.

 Changes to be committed:
	new file:   src/lufus/block_ops.py
	modified:   src/lufus/drives/find_usb.py
	modified:   src/lufus/drives/formatting.py
	modified:   src/lufus/drives/get_usb_info.py
	modified:   src/lufus/gui/gui.py
	new file:   src/lufus/iso9660.py
	modified:   src/lufus/writing/flash_usb.py
	modified:   src/lufus/writing/install_ventoy.py
	modified:   src/lufus/writing/windows/detect.py
	modified:   src/lufus/writing/windows/flash.py
	modified:   src/lufus/writing/windows/tweaks.py
	modified:   tests/test_find_usb.py
	modified:   tests/test_flash_usb_and_find_usb_fixes.py
	modified:   tests/test_formatting.py
	modified:   tests/test_get_usb_info.py
	modified:   tests/test_get_usb_info_and_detect_windows_fixes.py
	modified:   tests/test_linux_iso_detection.py
@Radiump123
Copy link
Copy Markdown
Owner Author

Gonna test the new DVD stuff later w/ my thinkPad

R. Larocque added 2 commits May 15, 2026 18:46
 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is ahead of 'origin/dev' by 1 commit.
   (use "git push" to publish your local commits)

 Changes to be committed:
	modified:   src/lufus/drives/formatting.py
	modified:   src/lufus/gui/gui.py
	modified:   src/lufus/gui/workers.py
	modified:   src/lufus/state.py
	modified:   tests/test_states_and_autodetect_fixes.py
@Radiump123
Copy link
Copy Markdown
Owner Author

I think I fixed Hogjects#206

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

New security issues found

(mkfs.*, wimlib-imagex, package managers).
"""
try:
return subprocess.run(cmd, check=check)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

… needs testing.

 Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch dev
 Your branch is up to date with 'origin/dev'.

 Changes to be committed:
	new file:   scripts/build-standalone.sh
	modified:   src/lufus/block_ops.py
	modified:   src/lufus/drives/formatting.py
	modified:   src/lufus/gui/workers.py
@Radiump123
Copy link
Copy Markdown
Owner Author

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 security issue, 2 other issues, and left some high level feedback:

Security issues:

  • Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)

General comments:

  • The new _reset_terminal helper in gui.py calls tty.setraw and then immediately restores the previous settings; if the goal is a stty sane equivalent it would be more robust to explicitly configure canonical/echo flags (and to no-op safely when stdin is not a TTY) rather than toggling raw mode briefly.
  • The pure-Python ISO 9660 walker in iso9660.py currently only recurses shallowly and has comments about limited edge-case handling; if you expect to handle images with deeper directory trees or more complex layouts, consider either documenting these limits clearly in the detection functions or extending the traversal logic so detection doesn’t silently miss markers in nested paths.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `_reset_terminal` helper in `gui.py` calls `tty.setraw` and then immediately restores the previous settings; if the goal is a `stty sane` equivalent it would be more robust to explicitly configure canonical/echo flags (and to no-op safely when stdin is not a TTY) rather than toggling raw mode briefly.
- The pure-Python ISO 9660 walker in `iso9660.py` currently only recurses shallowly and has comments about limited edge-case handling; if you expect to handle images with deeper directory trees or more complex layouts, consider either documenting these limits clearly in the detection functions or extending the traversal logic so detection doesn’t silently miss markers in nested paths.

## Individual Comments

### Comment 1
<location path="src/lufus/gui/gui.py" line_range="109-118" />
<code_context>
+    return len(pids)
+
+
+def _reset_terminal() -> None:
+    """Reset terminal to sane settings via termios (replaces stty sane)."""
+    import termios
+    import tty
+
+    fd = sys.stdin.fileno()
+    settings = termios.tcgetattr(fd)
+    # Restore defaults: echo on, canonical mode, etc.
+    tty.setraw(fd, when=termios.TCSAFLUSH)
+    termios.tcsetattr(fd, termios.TCSANOW, settings)
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Terminal reset helper likely does nothing useful and may misbehave on non-TTY stdin.

Because you immediately restore `settings` after `tty.setraw`, the raw mode is never actually in effect, so this function doesn’t meaningfully “reset” anything. In addition, `sys.stdin.fileno()` can fail or misbehave when stdin isn’t a TTY (e.g., redirection, some GUI launches).

If this helper is meant to repair a broken terminal, consider targeting `/dev/tty` (or at least guarding with `os.isatty(fd)`) and restoring a known-good canonical/echo configuration captured at program start, rather than briefly toggling raw mode and reverting.
</issue_to_address>

### Comment 2
<location path="src/lufus/block_ops.py" line_range="228-231" />
<code_context>
+_GPT_PARTITIONS_DEFAULT_COUNT = 128  # standard GPT allows up to 128 entries
+
+# Partition type GUIDs (binary, little-endian format)
+_PART_TYPE_GUID = {
+    "data": bytes.fromhex("a2a0d0ebe5b9334487c068b6b72699c7"),  # Microsoft Basic Data
+    "efi": bytes.fromhex("28732ac11ff8d211ba4b00a0c93ec93b"),  # EFI System
+    "bios": bytes.fromhex("614886496e6f44746961676f6e6100"),  # BIOS Boot
+}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** BIOS boot partition type GUID and GPT layout look non-spec-compliant and may confuse firmware or tools.

The GUID and on-disk layout logic need correction:

* The `"bios"` GUID hex (`"614886496e6f44746961676f6e6100"`) is only 30 hex chars (15 bytes) and doesn’t match the standard BIOS Boot type GUID (`21686148-6449-6E6F-744E-656564454649`). `bytes.fromhex` will accept it, but GPT type GUIDs must be 16 bytes; this will create malformed partition entries.
* In `_write_gpt`, the backup header’s `partition_entries_lba` currently points to the primary entries, but you write the backup entries near the end of the disk. The backup header must point to the backup partition entry array, per the GPT spec.
* The protective MBR entry layout is nonstandard: `mbr += struct.pack("<I", 0x00000200)` at offset 446 overwrites the CHS and type fields instead of writing a proper 16‑byte partition entry.

Given that this code prepares real disks, I’d strongly recommend either fixing the GUID constants and strictly following the UEFI GPT/MBR layouts, or delegating partition table creation to a well-tested library/wrapper to avoid subtle on-disk corruption.
</issue_to_address>

### Comment 3
<location path="src/lufus/writing/windows/flash.py" line_range="34" />
<code_context>
        return subprocess.run(cmd, check=check)
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/lufus/gui/gui.py
Comment on lines +109 to +118
def _reset_terminal() -> None:
"""Reset terminal to sane settings via termios (replaces stty sane)."""
import termios
import tty

fd = sys.stdin.fileno()
settings = termios.tcgetattr(fd)
# Restore defaults: echo on, canonical mode, etc.
tty.setraw(fd, when=termios.TCSAFLUSH)
termios.tcsetattr(fd, termios.TCSANOW, settings)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Terminal reset helper likely does nothing useful and may misbehave on non-TTY stdin.

Because you immediately restore settings after tty.setraw, the raw mode is never actually in effect, so this function doesn’t meaningfully “reset” anything. In addition, sys.stdin.fileno() can fail or misbehave when stdin isn’t a TTY (e.g., redirection, some GUI launches).

If this helper is meant to repair a broken terminal, consider targeting /dev/tty (or at least guarding with os.isatty(fd)) and restoring a known-good canonical/echo configuration captured at program start, rather than briefly toggling raw mode and reverting.

Comment thread src/lufus/block_ops.py
Comment on lines +228 to +231
_PART_TYPE_GUID = {
"data": bytes.fromhex("a2a0d0ebe5b9334487c068b6b72699c7"), # Microsoft Basic Data
"efi": bytes.fromhex("28732ac11ff8d211ba4b00a0c93ec93b"), # EFI System
"bios": bytes.fromhex("614886496e6f44746961676f6e6100"), # BIOS Boot
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): BIOS boot partition type GUID and GPT layout look non-spec-compliant and may confuse firmware or tools.

The GUID and on-disk layout logic need correction:

  • The "bios" GUID hex ("614886496e6f44746961676f6e6100") is only 30 hex chars (15 bytes) and doesn’t match the standard BIOS Boot type GUID (21686148-6449-6E6F-744E-656564454649). bytes.fromhex will accept it, but GPT type GUIDs must be 16 bytes; this will create malformed partition entries.
  • In _write_gpt, the backup header’s partition_entries_lba currently points to the primary entries, but you write the backup entries near the end of the disk. The backup header must point to the backup partition entry array, per the GPT spec.
  • The protective MBR entry layout is nonstandard: mbr += struct.pack("<I", 0x00000200) at offset 446 overwrites the CHS and type fields instead of writing a proper 16‑byte partition entry.

Given that this code prepares real disks, I’d strongly recommend either fixing the GUID constants and strictly following the UEFI GPT/MBR layouts, or delegating partition table creation to a well-tested library/wrapper to avoid subtle on-disk corruption.

(mkfs.*, wimlib-imagex, package managers).
"""
try:
return subprocess.run(cmd, check=check)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

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.

1 participant