Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate yaml.load and add FullLoader and UnsafeLoader classes #257

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 96 additions & 5 deletions lib/yaml/__init__.py
Expand Up @@ -8,14 +8,53 @@
from loader import *
from dumper import *

__version__ = '4.1'
__version__ = '3.13'

try:
from cyaml import *
__with_libyaml__ = True
except ImportError:
__with_libyaml__ = False


#------------------------------------------------------------------------------
# Warnings control
#------------------------------------------------------------------------------

# 'Global' warnings state:
_warnings_enabled = {
'YAMLLoadWarning': True,
}

# Get or set global warnings' state
def warnings(settings=None):
if settings is None:
return _warnings_enabled

if type(settings) is dict:
for key in settings:
if key in _warnings_enabled:
_warnings_enabled[key] = settings[key]

# Warn when load() is called without Loader=...
class YAMLLoadWarning(RuntimeWarning):
pass

def load_warning(method):
if _warnings_enabled['YAMLLoadWarning'] is False:
return

import warnings

message = (
"calling yaml.%s() without Loader=... is deprecated, as the "
"default Loader is unsafe. Please read "
"https://msg.pyyaml.org/load for full details."
) % method

warnings.warn(message, YAMLLoadWarning, stacklevel=3)

#------------------------------------------------------------------------------
def scan(stream, Loader=Loader):
"""
Scan a YAML stream and produce scanning tokens.
Expand Down Expand Up @@ -61,45 +100,97 @@ def compose_all(stream, Loader=Loader):
finally:
loader.dispose()

def load(stream, Loader=Loader):
def load(stream, Loader=None):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
"""
if Loader is None:
load_warning('load')
Loader = FullLoader

loader = Loader(stream)
try:
return loader.get_single_data()
finally:
loader.dispose()

def load_all(stream, Loader=Loader):
def load_all(stream, Loader=None):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
"""
if Loader is None:
load_warning('load_all')
Loader = FullLoader

loader = Loader(stream)
try:
while loader.check_data():
yield loader.get_data()
finally:
loader.dispose()

def full_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.

Resolve all tags except those known to be
unsafe on untrusted input.
"""
return load(stream, FullLoader)

def full_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.

Resolve all tags except those known to be
unsafe on untrusted input.
"""
return load_all(stream, FullLoader)

def safe_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
Resolve only basic YAML tags.

Resolve only basic YAML tags. This is known
to be safe for untrusted input.
"""
return load(stream, SafeLoader)

def safe_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
Resolve only basic YAML tags.

Resolve only basic YAML tags. This is known
to be safe for untrusted input.
"""
return load_all(stream, SafeLoader)

def unsafe_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.

Resolve all tags, even those known to be
unsafe on untrusted input.
"""
return load(stream, UnsafeLoader)

def unsafe_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.

Resolve all tags, even those known to be
unsafe on untrusted input.
"""
return load_all(stream, UnsafeLoader)

def emit(events, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None):
Expand Down
126 changes: 80 additions & 46 deletions lib/yaml/constructor.py
@@ -1,6 +1,12 @@

__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
'ConstructorError']
__all__ = [
'BaseConstructor',
'SafeConstructor',
'FullConstructor',
'UnsafeConstructor',
'Constructor',
'ConstructorError'
]

from error import *
from nodes import *
Expand Down Expand Up @@ -464,7 +470,7 @@ def construct_undefined(self, node):
SafeConstructor.add_constructor(None,
SafeConstructor.construct_undefined)

class Constructor(SafeConstructor):
class FullConstructor(SafeConstructor):

def construct_python_str(self, node):
return self.construct_scalar(node).encode('utf-8')
Expand All @@ -481,18 +487,22 @@ def construct_python_complex(self, node):
def construct_python_tuple(self, node):
return tuple(self.construct_sequence(node))

def find_python_module(self, name, mark):
def find_python_module(self, name, mark, unsafe=False):
if not name:
raise ConstructorError("while constructing a Python module", mark,
"expected non-empty name appended to the tag", mark)
try:
__import__(name)
except ImportError, exc:
if unsafe:
try:
__import__(name)
except ImportError, exc:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
if not name in sys.modules:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
"module %r is not imported" % name.encode('utf-8'), mark)
return sys.modules[name]

def find_python_name(self, name, mark):
def find_python_name(self, name, mark, unsafe=False):
if not name:
raise ConstructorError("while constructing a Python object", mark,
"expected non-empty name appended to the tag", mark)
Expand All @@ -501,11 +511,15 @@ def find_python_name(self, name, mark):
else:
module_name = '__builtin__'
object_name = name
try:
__import__(module_name)
except ImportError, exc:
if unsafe:
try:
__import__(module_name)
except ImportError, exc:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
if not module_name in sys.modules:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
"module %r is not imported" % module_name.encode('utf-8'), mark)
module = sys.modules[module_name]
if not hasattr(module, object_name):
raise ConstructorError("while constructing a Python object", mark,
Expand All @@ -532,12 +546,16 @@ def construct_python_module(self, suffix, node):
class classobj: pass

def make_python_instance(self, suffix, node,
args=None, kwds=None, newobj=False):
args=None, kwds=None, newobj=False, unsafe=False):
if not args:
args = []
if not kwds:
kwds = {}
cls = self.find_python_name(suffix, node.start_mark)
if not (unsafe or isinstance(cls, type) or isinstance(cls, type(self.classobj))):
raise ConstructorError("while constructing a Python instance", node.start_mark,
"expected a class, but found %r" % type(cls),
node.start_mark)
if newobj and isinstance(cls, type(self.classobj)) \
and not args and not kwds:
instance = self.classobj()
Expand Down Expand Up @@ -609,67 +627,83 @@ def construct_python_object_apply(self, suffix, node, newobj=False):
def construct_python_object_new(self, suffix, node):
return self.construct_python_object_apply(suffix, node, newobj=True)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/none',
Constructor.construct_yaml_null)
FullConstructor.construct_yaml_null)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/bool',
Constructor.construct_yaml_bool)
FullConstructor.construct_yaml_bool)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/str',
Constructor.construct_python_str)
FullConstructor.construct_python_str)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/unicode',
Constructor.construct_python_unicode)
FullConstructor.construct_python_unicode)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/int',
Constructor.construct_yaml_int)
FullConstructor.construct_yaml_int)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/long',
Constructor.construct_python_long)
FullConstructor.construct_python_long)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/float',
Constructor.construct_yaml_float)
FullConstructor.construct_yaml_float)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/complex',
Constructor.construct_python_complex)
FullConstructor.construct_python_complex)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/list',
Constructor.construct_yaml_seq)
FullConstructor.construct_yaml_seq)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/tuple',
Constructor.construct_python_tuple)
FullConstructor.construct_python_tuple)

Constructor.add_constructor(
FullConstructor.add_constructor(
u'tag:yaml.org,2002:python/dict',
Constructor.construct_yaml_map)
FullConstructor.construct_yaml_map)

Constructor.add_multi_constructor(
FullConstructor.add_multi_constructor(
u'tag:yaml.org,2002:python/name:',
Constructor.construct_python_name)
FullConstructor.construct_python_name)

Constructor.add_multi_constructor(
FullConstructor.add_multi_constructor(
u'tag:yaml.org,2002:python/module:',
Constructor.construct_python_module)
FullConstructor.construct_python_module)

Constructor.add_multi_constructor(
FullConstructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object:',
Constructor.construct_python_object)
FullConstructor.construct_python_object)

Constructor.add_multi_constructor(
FullConstructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object/apply:',
Constructor.construct_python_object_apply)
FullConstructor.construct_python_object_apply)

Constructor.add_multi_constructor(
FullConstructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object/new:',
Constructor.construct_python_object_new)
FullConstructor.construct_python_object_new)

class UnsafeConstructor(FullConstructor):

def find_python_module(self, name, mark):
return super(UnsafeConstructor, self).find_python_module(name, mark, unsafe=True)

def find_python_name(self, name, mark):
return super(UnsafeConstructor, self).find_python_name(name, mark, unsafe=True)

def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False):
return super(UnsafeConstructor, self).make_python_instance(
suffix, node, args, kwds, newobj, unsafe=True)

# Constructor is same as UnsafeConstructor. Need to leave this in place in case
# people have extended it directly.
class Constructor(UnsafeConstructor):
pass
20 changes: 18 additions & 2 deletions lib/yaml/cyaml.py
@@ -1,6 +1,8 @@

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

from _yaml import CParser, CEmitter

Expand All @@ -25,6 +27,20 @@ def __init__(self, stream):
SafeConstructor.__init__(self)
Resolver.__init__(self)

class CFullLoader(CParser, FullConstructor, Resolver):

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

class CUnsafeLoader(CParser, UnsafeConstructor, Resolver):

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

class CLoader(CParser, Constructor, Resolver):

def __init__(self, stream):
Expand Down