Skip to content

Commit

Permalink
fix: only prevent access to object paths containing __globals__ and _…
Browse files Browse the repository at this point in the history
…_builtins__ for non-dict/list objects

Fixes #180
  • Loading branch information
dgilland committed Feb 24, 2023
1 parent c1277f1 commit 2015f0a
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 10 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
Changelog
=========

- Only prevent access to object paths containing ``__globals__`` or ``__builtins__`` instead of all dunder-methods for non-dict/list objects.


v6.0.1 (2023-02-20)
-------------------

Expand Down
10 changes: 5 additions & 5 deletions src/pydash/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#: Dictionary of builtins with keys as the builtin function and values as the string name.
BUILTINS = {value: key for key, value in builtins.__dict__.items() if isinstance(value, Hashable)}

#: Object keys that are restricted from access via path access.
RESTRICTED_KEYS = ("__globals__", "__builtins__")


def callit(iteratee, *args, **kwargs):
"""Inspect argspec of `iteratee` function and only pass the supported arguments when calling
Expand Down Expand Up @@ -188,11 +191,8 @@ def _base_get_object(obj, key, default=UNSET):


def _raise_if_restricted_key(key):
if not isinstance(key, str):
return
# Prevent access to dunder-methods since this could expose access to globals through leaky
# attributes such as obj.__init__.__globals__.
if len(key) > 4 and key.isascii() and key.startswith("__") and key.endswith("__"):
# Prevent access to restricted keys for security reasons.
if key in RESTRICTED_KEYS:
raise KeyError(f"access to restricted key {key!r} is not allowed")


Expand Down
25 changes: 20 additions & 5 deletions tests/test_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,10 @@ def test_get__should_not_populate_defaultdict():
@parametrize(
"obj,path",
[
(helpers.Object(), "__init__"),
(helpers.Object(subobj=helpers.Object()), "subobj.__init__"),
(namedtuple("a", ["a"])(a=1), "__len__"),
(helpers.Object(), "__init__.__globals__"),
(namedtuple("a", ["a"])(a=1), "__globals__"),
(helpers.Object(subobj=helpers.Object()), "subobj.__builtins__"),
(helpers.Object(subobj=helpers.Object()), "__builtins__"),
],
)
def test_get__raises_for_objects_when_path_restricted(obj, path):
Expand All @@ -397,14 +398,28 @@ def test_get__raises_for_objects_when_path_restricted(obj, path):
@parametrize(
"obj,path",
[
({}, "__init__"),
([], "__contains__"),
({}, "__globals__"),
({}, "__builtins__"),
([], "__globals__"),
([], "__builtins__"),
],
)
def test_get__does_not_raise_for_dict_or_list_when_path_restricted(obj, path):
assert _.get(obj, path) is None


@parametrize(
"obj,path",
[
(helpers.Object(), "__name__"),
(helpers.Object(), "foo.__dict__"),
(helpers.Object(), "__len__"),
],
)
def test_get__does_not_raise_for_objects_when_path_is_unrestricted(obj, path):
assert _.get(obj, path) is None


@parametrize(
"case,expected",
[
Expand Down

0 comments on commit 2015f0a

Please sign in to comment.