Skip to content

Commit

Permalink
Fix find_commands permission error (conda#13086)
Browse files Browse the repository at this point in the history
  • Loading branch information
kenodegard authored Sep 11, 2023
1 parent e3fe940 commit ec90105
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 7 deletions.
17 changes: 10 additions & 7 deletions conda/cli/find_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
import sysconfig
from functools import lru_cache
from os.path import basename, expanduser, isdir, isfile, join
from os.path import basename, expanduser, isfile, join

from ..common.compat import on_win

Expand Down Expand Up @@ -61,16 +61,19 @@ def find_commands(include_others=True):
dir_paths.extend(os.environ.get("PATH", "").split(os.pathsep))

if on_win:
pat = re.compile(r"conda-([\w\-]+)\.(exe|bat)$")
pat = re.compile(r"conda-([\w\-]+)(\.(exe|bat))?$")
else:
pat = re.compile(r"conda-([\w\-]+)$")

res = set()
for dir_path in dir_paths:
if not isdir(dir_path):
try:
for entry in os.scandir(dir_path):
m = pat.match(entry.name)
if m and entry.is_file():
res.add(m.group(1))
except (FileNotFoundError, PermissionError):
# FileNotFoundError: directory doesn't exist
# PermissionError: user doesn't have read access
continue
for entry in os.scandir(dir_path):
m = pat.match(entry.name)
if m and entry.is_file():
res.add(m.group(1))
return tuple(sorted(res))
19 changes: 19 additions & 0 deletions news/13062-find-commands-permission-error
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Enhancements

* <news item>

### Bug fixes

* Catch `PermissionError` raised by `conda.cli.find_commands.find_commands` when user's `$PATH` contains restricted paths. (#13062)

### Deprecations

* <news item>

### Docs

* <news item>

### Other

* <news item>
71 changes: 71 additions & 0 deletions tests/cli/test_find_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
import os
from pathlib import Path

import pytest
from pytest import MonkeyPatch

from conda.cli.find_commands import find_commands, find_executable
from conda.common.compat import on_win


@pytest.fixture
def faux_path(tmp_path: Path, monkeypatch: MonkeyPatch) -> Path:
if not on_win:
# make a read-only location, none of these should show up in the tests
permission = tmp_path / "permission"
permission.mkdir(mode=0o333, exist_ok=True)
(permission / "conda-permission").touch()
(permission / "conda-permission.bat").touch()
(permission / "conda-permission.exe").touch()
monkeypatch.setenv("PATH", str(permission), prepend=os.pathsep)

# bad executables
bad = tmp_path / "bad"
bad.mkdir(exist_ok=True)
(bad / "non-conda-bad").touch()
(bad / "non-conda-bad.bat").touch()
(bad / "non-conda-bad.exe").touch()
monkeypatch.setenv("PATH", str(bad), prepend=os.pathsep)

# good executables
bin_ = tmp_path / "bin"
bin_.mkdir(exist_ok=True)
(bin_ / "conda-bin").touch()
monkeypatch.setenv("PATH", str(bin_), prepend=os.pathsep)

bat = tmp_path / "bat"
bat.mkdir(exist_ok=True)
(bat / "conda-bat.bat").touch()
monkeypatch.setenv("PATH", str(bat), prepend=os.pathsep)

exe = tmp_path / "exe"
exe.mkdir(exist_ok=True)
(exe / "conda-exe.exe").touch()
monkeypatch.setenv("PATH", str(exe), prepend=os.pathsep)

yield tmp_path

if not on_win:
# undo read-only for clean removal
permission.chmod(permission.stat().st_mode | 0o444)


def test_find_executable(faux_path: Path):
assert (faux_path / "bin" / "conda-bin").samefile(find_executable("conda-bin"))
if on_win:
assert (faux_path / "bat" / "conda-bat.bat").samefile(
find_executable("conda-bat")
)
assert (faux_path / "exe" / "conda-exe.exe").samefile(
find_executable("conda-exe")
)


def test_find_commands(faux_path: Path):
find_commands.cache_clear()
if on_win:
assert {"bin", "bat", "exe"}.issubset(find_commands())
else:
assert {"bin"}.issubset(find_commands())

0 comments on commit ec90105

Please sign in to comment.