Skip to content

Commit 4d30546

Browse files
authored
Add security warning (#3074)
* Add security protection for what can be instantiated * Add tests for instantiation blocklist * add news file for security blocklist
1 parent 4e50439 commit 4d30546

File tree

4 files changed

+112
-2
lines changed

4 files changed

+112
-2
lines changed

hydra/_internal/instantiate/_instantiate2.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import copy
44
import functools
5+
import os
56
from enum import Enum
67
from textwrap import dedent
78
from typing import Any, Callable, Dict, List, Sequence, Tuple, Union
@@ -13,6 +14,52 @@
1314
from hydra.errors import InstantiationException
1415
from hydra.types import ConvertMode, TargetConf
1516

17+
DEFAULT_BLOCKLISTED_MODULES = {
18+
"builtins.exec",
19+
"builtins.eval",
20+
"builtins.__import__",
21+
"builtins.exit",
22+
"builtins.quit",
23+
"os.environ.OMP_NUM_THREADS",
24+
"os.kill",
25+
"os.system",
26+
"os.putenv",
27+
"os.remove",
28+
"os.removedirs",
29+
"os.rmdir",
30+
"os.fchdir",
31+
"os.setuid",
32+
"os.fork",
33+
"os.forkpty",
34+
"os.killpg",
35+
"os.rename",
36+
"os.renames",
37+
"os.truncate",
38+
"os.replace",
39+
"os.unlink",
40+
"os.fchmod",
41+
"os.fchown",
42+
"os.chmod",
43+
"os.chown",
44+
"os.chroot",
45+
"os.fchdir",
46+
"os.lchflags",
47+
"os.lchmod",
48+
"os.lchown",
49+
"os.getcwd",
50+
"os.chdir",
51+
"shutil.rmtree",
52+
"shutil.move",
53+
"shutil.chown",
54+
"subprocess.Popen",
55+
"builtins.help",
56+
"sys.modules.ipdb",
57+
"sys.modules.joblib",
58+
"sys.modules.resource",
59+
"sys.modules.psutil",
60+
"sys.modules.tkinter",
61+
}
62+
1663

1764
class _Keys(str, Enum):
1865
"""Special keys in configs used by instantiate."""
@@ -130,6 +177,19 @@ def _resolve_target(
130177
) -> Union[type, Callable[..., Any]]:
131178
"""Resolve target string, type or callable into type or callable."""
132179
if isinstance(target, str):
180+
if target in DEFAULT_BLOCKLISTED_MODULES:
181+
allowlist = os.environ.get("HYDRA_INSTANTIATE_ALLOWLIST_OVERRIDE", "")
182+
if target not in allowlist.split(":"):
183+
msg = dedent(
184+
f"""\
185+
Target '{target}' is blocklisted and cannot be instantiated from config
186+
to prevent security vulnerabilities, set env var
187+
HYDRA_INSTANTIATE_ALLOWLIST_OVERRIDE={target}:<other allowlisted targets> to bypass"""
188+
)
189+
if full_key:
190+
msg += f"\nfull_key: {full_key}"
191+
raise InstantiationException(msg)
192+
133193
try:
134194
target = _locate(target)
135195
except Exception as e:
@@ -181,6 +241,9 @@ def instantiate(
181241
:param config: An config object describing what to call and what params to use.
182242
In addition to the parameters, the config must contain:
183243
_target_ : target class or callable name (str)
244+
IMPORTANT: This may pose a security risk since the config
245+
can be used to execute arbitrary code. Make sure to use this only
246+
with trusted configs or configure the allowlist/blocklist.
184247
And may contain:
185248
_args_: List-like of positional arguments to pass to the target
186249
_recursive_: Construct nested objects as well (bool).

news/3074.api_change

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Certain modules that may introduce a security vulnerability can no longer be instantiated by default. This is not exhaustive, do not rely on this to prevent configs from executing dangerous code.

tests/instantiate/test_instantiate.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,42 @@ def test_cannot_locate_target(instantiate_func: Any) -> None:
15981598
)
15991599

16001600

1601+
def test_blocklisted_target_fails(instantiate_func: Any) -> None:
1602+
cfg = OmegaConf.create({"foo": {"_target_": "os.getcwd"}})
1603+
with raises(
1604+
InstantiationException,
1605+
match=re.escape(
1606+
dedent(
1607+
"""\
1608+
Target 'os.getcwd' is blocklisted and cannot be instantiated from config
1609+
to prevent security vulnerabilities, set env var
1610+
HYDRA_INSTANTIATE_ALLOWLIST_OVERRIDE=os.getcwd:<other allowlisted targets> to bypass
1611+
full_key: foo"""
1612+
)
1613+
),
1614+
) as exc_info:
1615+
instantiate_func(cfg)
1616+
err = exc_info.value
1617+
assert hasattr(err, "__cause__")
1618+
chained = err.__cause__
1619+
assert chained is None
1620+
1621+
1622+
def test_allowlist_works(instantiate_func: Any, monkeypatch: Any) -> None:
1623+
cfg = OmegaConf.create(
1624+
{
1625+
"foo": {"_target_": "builtins.exec", "_args_": ["5+8"]},
1626+
"bar": {"_target_": "builtins.eval", "_args_": ["1+2"]},
1627+
}
1628+
)
1629+
monkeypatch.setenv(
1630+
"HYDRA_INSTANTIATE_ALLOWLIST_OVERRIDE", "builtins.exec:builtins.eval"
1631+
)
1632+
res = instantiate_func(cfg)
1633+
assert res.foo is None
1634+
assert res.bar == 3
1635+
1636+
16011637
@mark.parametrize(
16021638
"primitive,expected_primitive",
16031639
[

website/docs/advanced/instantiate_objects/overview.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ Call/instantiate supports:
1818
- Constructing an object by calling the `__init__` method
1919
- Calling functions, static functions, class methods and other callable global objects
2020

21+
22+
:::warning
23+
This may pose a security risk since the config can be used to execute arbitrary code. Make
24+
sure to use this only with trusted configs.
25+
26+
There is a default list of blocklisted modules. To bypass it, set the env var
27+
HYDRA_INSTANTIATE_ALLOWLIST_OVERRIDE with a colon-separated list of modules to allowlist,
28+
eg. `HYDRA_INSTANTIATE_ALLOWLIST_OVERRIDE=os.rename:shutil.move`
29+
:::
30+
2131
<details><summary>Instantiate API (Expand for details)</summary>
2232

2333
```python
@@ -178,7 +188,7 @@ Similarly, positional arguments of nested objects can be overridden:
178188
obj = instantiate(
179189
cfg.object,
180190
# pass 1 and 2 as positional arguments to the target object
181-
1, 2,
191+
1, 2,
182192
# pass 3 and 4 as positional arguments to a nested child object
183193
child={"_args_": [3, 4]},
184194
)
@@ -295,7 +305,7 @@ obj_all = instantiate(cfg_all)
295305

296306
### Partial Instantiation
297307
Sometimes you may not set all parameters needed to instantiate an object from the configuration, in this case you can set
298-
`_partial_` to be `True` to get a `functools.partial` wrapped object or method, then complete initializing the object in
308+
`_partial_` to be `True` to get a `functools.partial` wrapped object or method, then complete initializing the object in
299309
the application code. Here is an example:
300310

301311
```python title="Example classes"

0 commit comments

Comments
 (0)