fix: prefer the pinned bundled adb over one found on PATH

This commit is contained in:
dime-online committed 2026-10-06 21:15:31 -04:00
1 parent baaddad2ab
commit bb43a64d3d
2 files changed
+64 -5

No files matched your search

+11 -5
View File
@@ -82,17 +82,23 @@ def resolve_adb(selected: str | None = None) -> Path:
if not path.is_file() or path.name.lower() not in ("adb", "adb.exe"):
raise USBError("Select adb.exe from the Android SDK platform-tools folder.")
return path
# Prefer an existing client to cooperate with other applications' ADB server.
existing = shutil.which("adb")
if existing:
return Path(existing).resolve()
if sys.platform == "win32":
if sys.platform == "win32" or getattr(sys, "frozen", False):
# Only the hash-verified bundle is known to be the build shipped with
# this installer. The search path (which on Windows also covers the
# working directory) can surface an unrelated or shadowing adb.exe, so
# it is never trusted while a verified tool is available. The bundle
# is consulted on every platform it ships on, not just Windows.
included = bundled_adb()
if included:
return included
sdk = Path(os.environ.get("LOCALAPPDATA", str(Path.home()))) / "Android" / "Sdk" / "platform-tools" / "adb.exe"
if sdk.is_file():
return sdk
# A client found on the search path may cooperate with another
# application's ADB server, but its provenance is unknown: last resort.
existing = shutil.which("adb")
if existing:
return Path(existing).resolve()
raise USBError("USB tools were not found. Install Google's Android SDK Platform Tools and select adb.exe in Advanced settings.")
+53
View File
@@ -2,6 +2,7 @@ import hashlib
import json
import socket
import subprocess
import sys
from unittest.mock import Mock
import paramiko
@@ -291,3 +292,55 @@ def test_bundled_helper_and_notices_are_verified_then_cached(tmp_path, monkeypat
def test_unknown_transport_rejected():
with pytest.raises(ValueError, match='Choose'):
Settings('frame', 'secret', transport='other')
def _install_fake_bundle(tmp_path, monkeypatch):
root = tmp_path / 'bundle' / 'resources' / 'usb'
root.mkdir(parents=True)
files = {name: hashlib.sha256(name.encode()).hexdigest() for name in usb.ADB_FILES}
for name in (*usb.ADB_FILES, 'NOTICE.txt'):
(root / name).write_bytes(name.encode())
manifest = {'version': '37.0.1', 'files': files, 'noticeSha256': hashlib.sha256(b'NOTICE.txt').hexdigest()}
(root / 'manifest.json').write_text(json.dumps(manifest))
monkeypatch.setattr(usb.sys, 'frozen', True, raising=False)
monkeypatch.setattr(usb.sys, '_MEIPASS', str(root.parent.parent), raising=False)
monkeypatch.setenv('LOCALAPPDATA', str(tmp_path / 'local'))
return tmp_path / 'local' / 'HaloFrameInstaller' / 'usb-tools' / '37.0.1' / 'adb.exe'
def _expose_adb_on_search_path(tmp_path, monkeypatch):
folder = tmp_path / 'search-path'
folder.mkdir()
# shutil.which("adb") needs an executable file named exactly "adb" on
# POSIX and finds "adb.exe" via the Windows suffix rules, so provide both.
for name in ('adb', 'adb.exe'):
shadow = folder / name
shadow.write_bytes(b'shadowing adb')
shadow.chmod(0o755)
# shutil.which on Windows searches the working directory before PATH.
monkeypatch.chdir(tmp_path)
monkeypatch.setenv('PATH', str(folder))
return (folder / ('adb.exe' if sys.platform == 'win32' else 'adb')).resolve()
def test_hash_verified_bundle_is_preferred_over_search_path_adb(tmp_path, monkeypatch):
bundled = _install_fake_bundle(tmp_path, monkeypatch)
_expose_adb_on_search_path(tmp_path, monkeypatch)
assert usb.resolve_adb() == bundled
def test_search_path_adb_is_used_when_no_bundle_exists(tmp_path, monkeypatch):
monkeypatch.setattr(usb.sys, 'frozen', True, raising=False)
monkeypatch.setattr(usb.sys, '_MEIPASS', str(tmp_path / 'no-bundle'), raising=False)
monkeypatch.setenv('LOCALAPPDATA', str(tmp_path / 'local'))
shadow = _expose_adb_on_search_path(tmp_path, monkeypatch)
assert usb.resolve_adb() == shadow
def test_explicitly_selected_adb_wins_over_bundle_and_search_path(tmp_path, monkeypatch):
_install_fake_bundle(tmp_path, monkeypatch)
_expose_adb_on_search_path(tmp_path, monkeypatch)
chosen = tmp_path / 'chosen' / 'adb.exe'
chosen.parent.mkdir()
chosen.write_bytes(b'user selected adb')
assert usb.resolve_adb(str(chosen)) == chosen.resolve()