Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ For a complete changelog, see:

7.0.0.dev0 (TBD)

* TBD
* Remove `Loader` class as an alias to `UnsafeLoader`
* Add warning messages when using `UnsafeLoader`, `CUnsafeLoader`, `unsafe_load()`, and `unsafe_load_all()`
* Change default loader in `scan()`, `parse()`, `compose()`, and `compose_all()` from `UnsafeLoader` to `SafeLoader`

6.0.2 (2024-08-06)

Expand Down
55 changes: 34 additions & 21 deletions lib/yaml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@
from .events import *
from .nodes import *

from .loader import *
from .loader import BaseLoader, FullLoader, SafeLoader, UnsafeLoader
from .dumper import *

__version__ = '7.0.0.dev0'
try:
from .cyaml import *
from .cyaml import CBaseLoader, CSafeLoader, CFullLoader, CUnsafeLoader, CBaseDumper, CSafeDumper, CDumper
__with_libyaml__ = True
except ImportError:
__with_libyaml__ = False

import io
import warnings

#------------------------------------------------------------------------------
# XXX "Warnings control" is now deprecated. Leaving in the API function to not
Expand All @@ -26,7 +27,7 @@ def warnings(settings=None):
return {}

#------------------------------------------------------------------------------
def scan(stream, Loader=Loader):
def scan(stream, Loader=SafeLoader):
"""
Scan a YAML stream and produce scanning tokens.
"""
Expand All @@ -37,7 +38,7 @@ def scan(stream, Loader=Loader):
finally:
loader.dispose()

def parse(stream, Loader=Loader):
def parse(stream, Loader=SafeLoader):
"""
Parse a YAML stream and produce parsing events.
"""
Expand All @@ -48,7 +49,7 @@ def parse(stream, Loader=Loader):
finally:
loader.dispose()

def compose(stream, Loader=Loader):
def compose(stream, Loader=SafeLoader):
"""
Parse the first YAML document in a stream
and produce the corresponding representation tree.
Expand All @@ -59,7 +60,7 @@ def compose(stream, Loader=Loader):
finally:
loader.dispose()

def compose_all(stream, Loader=Loader):
def compose_all(stream, Loader=SafeLoader):
"""
Parse all YAML documents in a stream
and produce corresponding representation trees.
Expand Down Expand Up @@ -141,7 +142,15 @@ def unsafe_load(stream):

Resolve all tags, even those known to be
unsafe on untrusted input.
"""

WARNING: This function is dangerous and can execute arbitrary code.
Only use on trusted input! Use safe_load() or full_load() instead.
"""
warnings.warn(
"unsafe_load() is dangerous and can execute arbitrary code when loading untrusted YAML data. "
"Use safe_load() or full_load() instead.",
RuntimeWarning, stacklevel=2
)
return load(stream, UnsafeLoader)

def unsafe_load_all(stream):
Expand All @@ -151,7 +160,15 @@ def unsafe_load_all(stream):

Resolve all tags, even those known to be
unsafe on untrusted input.
"""

WARNING: This function is dangerous and can execute arbitrary code.
Only use on trusted input! Use safe_load_all() or full_load_all() instead.
"""
warnings.warn(
"unsafe_load_all() is dangerous and can execute arbitrary code when loading untrusted YAML data. "
"Use safe_load_all() or full_load_all() instead.",
RuntimeWarning, stacklevel=2
)
return load_all(stream, UnsafeLoader)

def emit(events, stream=None, Dumper=Dumper,
Expand Down Expand Up @@ -277,9 +294,8 @@ def add_implicit_resolver(tag, regexp, first=None,
first is a sequence of possible initial characters or None.
"""
if Loader is None:
loader.Loader.add_implicit_resolver(tag, regexp, first)
loader.FullLoader.add_implicit_resolver(tag, regexp, first)
loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first)
FullLoader.add_implicit_resolver(tag, regexp, first)
UnsafeLoader.add_implicit_resolver(tag, regexp, first)
else:
Loader.add_implicit_resolver(tag, regexp, first)
Dumper.add_implicit_resolver(tag, regexp, first)
Expand All @@ -292,9 +308,8 @@ def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper):
Keys can be string values, integers, or None.
"""
if Loader is None:
loader.Loader.add_path_resolver(tag, path, kind)
loader.FullLoader.add_path_resolver(tag, path, kind)
loader.UnsafeLoader.add_path_resolver(tag, path, kind)
FullLoader.add_path_resolver(tag, path, kind)
UnsafeLoader.add_path_resolver(tag, path, kind)
else:
Loader.add_path_resolver(tag, path, kind)
Dumper.add_path_resolver(tag, path, kind)
Expand All @@ -306,9 +321,8 @@ def add_constructor(tag, constructor, Loader=None):
and a node object and produces the corresponding Python object.
"""
if Loader is None:
loader.Loader.add_constructor(tag, constructor)
loader.FullLoader.add_constructor(tag, constructor)
loader.UnsafeLoader.add_constructor(tag, constructor)
FullLoader.add_constructor(tag, constructor)
UnsafeLoader.add_constructor(tag, constructor)
else:
Loader.add_constructor(tag, constructor)

Expand All @@ -320,9 +334,8 @@ def add_multi_constructor(tag_prefix, multi_constructor, Loader=None):
and a node object and produces the corresponding Python object.
"""
if Loader is None:
loader.Loader.add_multi_constructor(tag_prefix, multi_constructor)
loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor)
loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor)
FullLoader.add_multi_constructor(tag_prefix, multi_constructor)
UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor)
else:
Loader.add_multi_constructor(tag_prefix, multi_constructor)

Expand Down Expand Up @@ -367,7 +380,7 @@ class YAMLObject(metaclass=YAMLObjectMetaclass):

__slots__ = () # no direct instantiation, so allow immutable subclasses

yaml_loader = [Loader, FullLoader, UnsafeLoader]
yaml_loader = [FullLoader, UnsafeLoader]
yaml_dumper = Dumper

yaml_tag = None
Expand Down
15 changes: 7 additions & 8 deletions lib/yaml/cyaml.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

__all__ = [
'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader',
'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader',
'CBaseDumper', 'CSafeDumper', 'CDumper'
]

Expand All @@ -12,6 +12,7 @@
from .representer import *

from .resolver import *
import warnings

class CBaseLoader(CParser, BaseConstructor, BaseResolver):

Expand All @@ -37,17 +38,15 @@ def __init__(self, stream):
class CUnsafeLoader(CParser, UnsafeConstructor, Resolver):

def __init__(self, stream):
warnings.warn(
"CUnsafeLoader is unsafe and can execute arbitrary code when loading untrusted YAML data. "
"Use CSafeLoader or CFullLoader instead.",
RuntimeWarning, stacklevel=2
)
CParser.__init__(self, stream)
UnsafeConstructor.__init__(self)
Resolver.__init__(self)

class CLoader(CParser, Constructor, Resolver):

def __init__(self, stream):
CParser.__init__(self, stream)
Constructor.__init__(self)
Resolver.__init__(self)

class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver):

def __init__(self, stream,
Expand Down
24 changes: 9 additions & 15 deletions lib/yaml/loader.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@

__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'Loader', 'UnsafeLoader']
__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'UnsafeLoader']

from .reader import *
from .scanner import *
from .parser import *
from .composer import *
from .constructor import *
from .resolver import *
import warnings

class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):

Expand Down Expand Up @@ -38,23 +39,16 @@ def __init__(self, stream):
SafeConstructor.__init__(self)
Resolver.__init__(self)

class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):

def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
Constructor.__init__(self)
Resolver.__init__(self)

# UnsafeLoader is the same as Loader (which is and was always unsafe on
# untrusted input). Use of either Loader or UnsafeLoader should be rare, since
# FullLoad should be able to load almost all YAML safely. Loader is left intact
# to ensure backwards compatibility.
# UnsafeLoader is unsafe on untrusted input. Use should be rare, since
# FullLoader should be able to load almost all YAML safely.
class UnsafeLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver):

def __init__(self, stream):
warnings.warn(
"UnsafeLoader is unsafe and can execute arbitrary code when loading untrusted YAML data. "
"Use SafeLoader or FullLoader instead.",
RuntimeWarning, stacklevel=2
)
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Expand Down
6 changes: 3 additions & 3 deletions tests/legacy_tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_structure(data_filename, structure_filename, verbose=False):
nodes2 = eval(file.read())
try:
with open(data_filename, 'rb') as file:
loader = yaml.Loader(file)
loader = yaml.UnsafeLoader(file)
while loader.check_event():
if loader.check_event(
yaml.StreamStartEvent, yaml.StreamEndEvent,
Expand Down Expand Up @@ -144,9 +144,9 @@ def test_composer(data_filename, canonical_filename, verbose=False):
def _make_loader():
global MyLoader

class MyLoader(yaml.Loader):
class MyLoader(yaml.UnsafeLoader):
def construct_sequence(self, node):
return tuple(yaml.Loader.construct_sequence(self, node))
return tuple(yaml.UnsafeLoader.construct_sequence(self, node))
def construct_mapping(self, node):
pairs = self.construct_pairs(node)
pairs.sort(key=(lambda i: str(i)))
Expand Down
8 changes: 4 additions & 4 deletions tests/legacy_tests/test_yaml_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

yaml.PyBaseLoader = yaml.BaseLoader
yaml.PySafeLoader = yaml.SafeLoader
yaml.PyLoader = yaml.Loader
yaml.PyLoader = yaml.UnsafeLoader
yaml.PyBaseDumper = yaml.BaseDumper
yaml.PySafeDumper = yaml.SafeDumper
yaml.PyDumper = yaml.Dumper
Expand Down Expand Up @@ -71,7 +71,7 @@ def new_safe_dump_all(documents, stream=None, **kwds):
def _set_up():
yaml.BaseLoader = yaml.CBaseLoader
yaml.SafeLoader = yaml.CSafeLoader
yaml.Loader = yaml.CLoader
yaml.UnsafeLoader = yaml.CUnsafeLoader
yaml.BaseDumper = yaml.CBaseDumper
yaml.SafeDumper = yaml.CSafeDumper
yaml.Dumper = yaml.CDumper
Expand All @@ -94,7 +94,7 @@ def _set_up():
def _tear_down():
yaml.BaseLoader = yaml.PyBaseLoader
yaml.SafeLoader = yaml.PySafeLoader
yaml.Loader = yaml.PyLoader
yaml.UnsafeLoader = yaml.PyLoader
yaml.BaseDumper = yaml.PyBaseDumper
yaml.SafeDumper = yaml.PySafeDumper
yaml.Dumper = yaml.PyDumper
Expand Down Expand Up @@ -251,7 +251,7 @@ def test_large_file(verbose=False):
for i in range(2**(SIZE_FILE-SIZE_ITERATION-SIZE_LINE) + 1):
temp_file.write(bytes(('-' + (' ' * (2**SIZE_LINE-4))+ '{}\n')*(2**SIZE_ITERATION), 'utf-8'))
temp_file.seek(0)
yaml.load(temp_file, Loader=yaml.CLoader)
yaml.load(temp_file, Loader=yaml.CUnsafeLoader)

test_large_file.unittest = None

Expand Down