3454a55636
cffconvert / validate (push) Has been cancelled
ci-workflow / pre-commit (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (macos-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (ubuntu-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (windows-latest) (push) Has been cancelled
ci-workflow / Python 3.10 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.11 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.12 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.13 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14t on macos-latest (push) Has been cancelled
ci-workflow / Python 3.10 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.11 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.12 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.13 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14t on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.10 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.11 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.12 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.13 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.14 on windows-latest (push) Has been cancelled
83 lines
3.3 KiB
Python
83 lines
3.3 KiB
Python
"""Regression tests for the untrusted-search-path fix in MaltParser (CWE-426).
|
|
|
|
``find_maltparser`` used ``os.path.exists(parser_dirname)`` to accept a directory
|
|
as-is. For a *relative* ``parser_dirname`` (as NLTK's docs use) that resolves
|
|
against the current working directory and is checked *before* the ``MALT_PARSER``
|
|
environment variable, so an attacker who can write a ``maltparser-*/`` directory
|
|
into the CWD could place its jars on the Java classpath -- overriding a trusted
|
|
``MALT_PARSER`` -- and have ``org.maltparser.Malt`` executed (RCE).
|
|
|
|
Only an explicit *absolute* directory is now taken as-is; a relative name is
|
|
resolved through ``MALT_PARSER``.
|
|
"""
|
|
|
|
import os
|
|
import pathlib
|
|
|
|
import pytest
|
|
|
|
from nltk.parse.malt import find_maltparser
|
|
|
|
# The names find_maltparser asserts must be present in the directory.
|
|
_MALT_JARS = ["log4j.jar", "libsvm.jar", "liblinear-1.8.jar", "maltparser-1.9.2.jar"]
|
|
|
|
|
|
def _make_malt_dir(path):
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
for jar in _MALT_JARS:
|
|
(path / jar).write_bytes(b"")
|
|
return path
|
|
|
|
|
|
def test_relative_dirname_not_resolved_against_cwd(tmp_path, monkeypatch):
|
|
"""A relative parser_dirname matching a CWD directory must NOT be used."""
|
|
cwd = tmp_path / "cwd"
|
|
cwd.mkdir()
|
|
_make_malt_dir(cwd / "maltparser-1.9.2") # attacker-planted in the CWD
|
|
monkeypatch.chdir(cwd)
|
|
monkeypatch.delenv("MALT_PARSER", raising=False)
|
|
|
|
# With no MALT_PARSER configured, the relative name must not fall back to the
|
|
# current working directory; discovery fails instead of silently using it.
|
|
with pytest.raises(LookupError):
|
|
find_maltparser("maltparser-1.9.2")
|
|
|
|
|
|
def test_cwd_does_not_override_configured_malt_parser(tmp_path, monkeypatch):
|
|
"""A CWD directory must not shadow a configured MALT_PARSER."""
|
|
cwd = tmp_path / "cwd"
|
|
cwd.mkdir()
|
|
_make_malt_dir(cwd / "maltparser-1.9.2") # attacker-planted in the CWD
|
|
trusted = _make_malt_dir(tmp_path / "trusted") # the configured location
|
|
monkeypatch.chdir(cwd)
|
|
monkeypatch.setenv("MALT_PARSER", str(trusted))
|
|
|
|
jars = find_maltparser("maltparser-1.9.2")
|
|
chosen = os.path.realpath(os.path.dirname(jars[0]))
|
|
assert chosen == os.path.realpath(
|
|
str(trusted)
|
|
), "CWD directory overrode the trusted MALT_PARSER"
|
|
|
|
|
|
def test_absolute_path_still_accepted(tmp_path, monkeypatch):
|
|
"""An explicit absolute directory is still used as-is."""
|
|
abs_dir = _make_malt_dir(tmp_path / "absmalt")
|
|
monkeypatch.delenv("MALT_PARSER", raising=False)
|
|
|
|
jars = find_maltparser(str(abs_dir))
|
|
chosen = os.path.realpath(os.path.dirname(jars[0]))
|
|
assert chosen == os.path.realpath(str(abs_dir))
|
|
|
|
|
|
def test_pathlike_argument_is_handled(tmp_path, monkeypatch):
|
|
"""A pathlib.Path argument is accepted; a relative one fails cleanly."""
|
|
abs_dir = _make_malt_dir(tmp_path / "absmalt")
|
|
monkeypatch.delenv("MALT_PARSER", raising=False)
|
|
# An absolute Path is accepted (normalised via os.fspath).
|
|
assert find_maltparser(pathlib.Path(abs_dir))
|
|
# A relative Path with no MALT_PARSER raises a clean LookupError -- not an
|
|
# AssertionError from find_dir()'s str check, and not a CWD pickup.
|
|
monkeypatch.chdir(tmp_path)
|
|
with pytest.raises(LookupError):
|
|
find_maltparser(pathlib.Path("not-a-configured-malt-dir"))
|