Skip to content

Commit

Permalink
Apply FullLoader/UnsafeLoader changes to lib3
Browse files Browse the repository at this point in the history
  • Loading branch information
perlpunk authored and ingydotnet committed Mar 8, 2019
1 parent 2d4d568 commit 2869cea
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 66 deletions.
100 changes: 95 additions & 5 deletions lib3/yaml/__init__.py
Expand Up @@ -8,7 +8,7 @@
from .loader import *
from .dumper import *

__version__ = '4.1'
__version__ = '3.13'
try:
from .cyaml import *
__with_libyaml__ = True
Expand All @@ -17,6 +17,44 @@

import io

#------------------------------------------------------------------------------
# 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 @@ -62,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
130 changes: 82 additions & 48 deletions lib3/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)
Expand Down Expand Up @@ -497,18 +503,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 as exc:
if unsafe:
try:
__import__(name)
except ImportError as exc:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name, exc), mark)
if not name in sys.modules:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name, exc), mark)
"module %r is not imported" % name, 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 @@ -517,11 +527,15 @@ def find_python_name(self, name, mark):
else:
module_name = 'builtins'
object_name = name
try:
__import__(module_name)
except ImportError as exc:
if unsafe:
try:
__import__(module_name)
except ImportError as exc:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name, exc), mark)
if not module_name in sys.modules:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name, exc), mark)
"module %r is not imported" % module_name, mark)
module = sys.modules[module_name]
if not hasattr(module, object_name):
raise ConstructorError("while constructing a Python object", mark,
Expand All @@ -544,12 +558,16 @@ def construct_python_module(self, suffix, node):
return self.find_python_module(suffix, node.start_mark)

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)):
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):
return cls.__new__(cls, *args, **kwds)
else:
Expand Down Expand Up @@ -616,71 +634,87 @@ 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(
'tag:yaml.org,2002:python/none',
Constructor.construct_yaml_null)
FullConstructor.construct_yaml_null)

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

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

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

Constructor.add_constructor(
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/bytes',
Constructor.construct_python_bytes)
FullConstructor.construct_python_bytes)

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

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

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

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

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

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

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

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

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

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

Constructor.add_multi_constructor(
FullConstructor.add_multi_constructor(
'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(
'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

0 comments on commit 2869cea

Please sign in to comment.