Skip to content

Commit 5c0e684

Browse files
authored
Prevent installed packages using invalid entrypoint names to write outside of aliases directory (#343)
1 parent a6a7bec commit 5c0e684

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

src/manage/aliasutils.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .exceptions import FilesInUseError, NoLauncherTemplateError
44
from .fsutils import atomic_unlink, ensure_tree, unlink
55
from .logging import LOGGER
6-
from .pathutils import Path, relative_to
6+
from .pathutils import Path, PurePath, relative_to
77
from .tagutils import install_matches_any
88

99
_EXE = ".exe".casefold()
@@ -97,6 +97,10 @@ def _create_alias(
9797
allow_link=True,
9898
_link=os.link):
9999
p = cmd.global_dir / name
100+
# Raise exception if someone has tried to get us to write outside of the
101+
# intended directory
102+
if str(p.relative_to(cmd.global_dir)) != PurePath(name).name:
103+
raise ValueError(f"Invalid alias name: {name}")
100104
if not p.match("*.exe"):
101105
p = p.with_name(p.name + ".exe")
102106
if not isinstance(target, Path):
@@ -235,6 +239,9 @@ def _parse_entrypoint_line(line):
235239
name, sep, rest = line.partition("=")
236240
name = name.strip()
237241
if name and name[0].isalnum() and sep and rest:
242+
# "names" that have a parent directory/slash are invalid
243+
if PurePath(name).parent:
244+
return None, None, None
238245
mod, sep, rest = rest.partition(":")
239246
mod = mod.strip()
240247
if mod and sep and rest:
@@ -382,6 +389,14 @@ def create_aliases(cmd, aliases, *, allow_link=True, _create_alias=_create_alias
382389
else:
383390
LOGGER.debug("Skipping %s alias because "
384391
"the launcher template was not found.", alias.name)
392+
except Exception:
393+
if install_matches_any(alias.install, getattr(cmd, "tags", None)):
394+
LOGGER.warn("Skipping %s alias because an unexpected error "
395+
"occurred.", alias.name)
396+
LOGGER.debug("TRACEBACK", exc_info=True)
397+
else:
398+
LOGGER.debug("Skipping %s alias because an unexpected error "
399+
"occurred.", alias.name, exc_info=True)
385400

386401

387402

tests/test_alias.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ def test_write_script_alias(alias_checker):
117117
alias_checker.check_script(alias_checker.Cmd(), "1.0-32", "testA", windowed=0)
118118

119119

120+
@pytest.mark.parametrize("name", [
121+
"..\\evil_path",
122+
"dir\\..\\evil_path",
123+
"normal\\subdir",
124+
"C:\\absolute\\evil_path",
125+
])
126+
def test_write_invalid_alias_name(alias_checker, name):
127+
with pytest.raises(ValueError):
128+
alias_checker.check(alias_checker.Cmd(), "1.0-32", name, None)
129+
130+
120131
def test_write_alias_launcher_missing(fake_config, assert_log, tmp_path):
121132
fake_config.launcher_exe = tmp_path / "non-existent.exe"
122133
fake_config.default_platform = '-32'
@@ -255,6 +266,8 @@ def test_parse_entrypoint_line():
255266
(" name = mod : func ", ("name", "mod", "func")),
256267
("name=mod:func[extra]", ("name", "mod", "func")),
257268
("name=mod:func [extra]", ("name", "mod", "func")),
269+
("../name=mod:func", (None, None, None)),
270+
("name/../../../name=mod:func", (None, None, None)),
258271
]:
259272
assert expect == AU._parse_entrypoint_line(line)
260273

0 commit comments

Comments
 (0)