From cef2e497e87a94a1e412ad4c989129664de12323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tina=20M=C3=BCller?= Date: Sun, 1 Jul 2018 16:47:06 +0200 Subject: [PATCH 1/5] Make SafeLoader and Loader independent again --- lib/yaml/__init__.py | 29 +++++++++++++++++++++++++++-- lib/yaml/cyaml.py | 8 +++++++- lib/yaml/loader.py | 11 ++++++++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/yaml/__init__.py b/lib/yaml/__init__.py index 628eb747..d3d10a3f 100644 --- a/lib/yaml/__init__.py +++ b/lib/yaml/__init__.py @@ -74,7 +74,19 @@ def load(stream, Loader=Loader): return loader.get_single_data() finally: loader.dispose() -safe_load = load + +def safe_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + + By default resolve only basic YAML tags + """ + loader = SafeLoader(stream) + try: + return loader.get_single_data() + finally: + loader.dispose() def load_all(stream, Loader=Loader): """ @@ -90,7 +102,20 @@ def load_all(stream, Loader=Loader): yield loader.get_data() finally: loader.dispose() -safe_load_all = load_all + +def safe_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + + By default resolve only basic YAML tags + """ + loader = SafeLoader(stream) + try: + while loader.check_data(): + yield loader.get_data() + finally: + loader.dispose() def danger_load(stream): """ diff --git a/lib/yaml/cyaml.py b/lib/yaml/cyaml.py index 5371f636..7e423c51 100644 --- a/lib/yaml/cyaml.py +++ b/lib/yaml/cyaml.py @@ -24,7 +24,13 @@ def __init__(self, stream): CParser.__init__(self, stream) SafeConstructor.__init__(self) Resolver.__init__(self) -CSafeLoader = CLoader + +class CSafeLoader(CParser, SafeConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + SafeConstructor.__init__(self) + Resolver.__init__(self) class CDangerLoader(CParser, Constructor, Resolver): diff --git a/lib/yaml/loader.py b/lib/yaml/loader.py index 6b18527a..c17ca6d3 100644 --- a/lib/yaml/loader.py +++ b/lib/yaml/loader.py @@ -27,7 +27,16 @@ def __init__(self, stream): Composer.__init__(self) SafeConstructor.__init__(self) Resolver.__init__(self) -SafeLoader = Loader + +class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + SafeConstructor.__init__(self) + Resolver.__init__(self) class DangerLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): From bb3b6451b0d5b3abfa90cdc9ff0e0d6d77b4dd3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tina=20M=C3=BCller?= Date: Sun, 1 Jul 2018 16:48:07 +0200 Subject: [PATCH 2/5] Remove danger_ shortcuts --- lib/yaml/__init__.py | 32 -------------------------------- tests/lib/test_recursive.py | 6 +++--- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/lib/yaml/__init__.py b/lib/yaml/__init__.py index d3d10a3f..4d5ffc88 100644 --- a/lib/yaml/__init__.py +++ b/lib/yaml/__init__.py @@ -117,22 +117,6 @@ def safe_load_all(stream): finally: loader.dispose() -def danger_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - When used on untrusted input, can result in arbitrary code execution. - """ - return load(stream, DangerLoader) - -def danger_load_all(stream): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - When used on untrusted input, can result in arbitrary code execution. - """ - return load_all(stream, DangerLoader) - def emit(events, stream=None, Dumper=Dumper, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None): @@ -228,14 +212,6 @@ def dump_all(documents, stream=None, Dumper=Dumper, return getvalue() safe_dump_all = dump_all -def danger_dump_all(documents, stream=None, **kwds): - """ - Serialize a sequence of Python objects into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all(documents, stream, Dumper=DangerDumper, **kwds) - def dump(data, stream=None, Dumper=Dumper, **kwds): """ Serialize a Python object into a YAML stream. @@ -244,14 +220,6 @@ def dump(data, stream=None, Dumper=Dumper, **kwds): return dump_all([data], stream, Dumper=Dumper, **kwds) safe_dump = dump -def danger_dump(data, stream=None, **kwds): - """ - Serialize a Python object into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=DangerDumper, **kwds) - def add_implicit_resolver(tag, regexp, first=None, Loader=Loader, Dumper=Dumper): """ diff --git a/tests/lib/test_recursive.py b/tests/lib/test_recursive.py index c67c1700..ccc9f345 100644 --- a/tests/lib/test_recursive.py +++ b/tests/lib/test_recursive.py @@ -29,9 +29,9 @@ def test_recursive(recursive_filename, verbose=False): value2 = None output2 = None try: - output1 = yaml.danger_dump(value1) - value2 = yaml.danger_load(output1) - output2 = yaml.danger_dump(value2) + output1 = yaml.dump(value1, Dumper=yaml.DangerDumper) + value2 = yaml.load(output1, Loader=yaml.DangerLoader) + output2 = yaml.dump(value2, Dumper=yaml.DangerDumper) assert output1 == output2, (output1, output2) finally: if verbose: From 59138c9239f892ace695c6a37604c72f95baad4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tina=20M=C3=BCller?= Date: Sun, 1 Jul 2018 21:32:16 +0200 Subject: [PATCH 3/5] Add example --- examples/safe.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/safe.py diff --git a/examples/safe.py b/examples/safe.py new file mode 100644 index 00000000..7ff87332 --- /dev/null +++ b/examples/safe.py @@ -0,0 +1,30 @@ +import yaml +import os + +def myconstructor(loader, node): + value = loader.construct_scalar(node) + os.system("echo hello " + value) + return value + +yaml.Loader.add_constructor(u'!hello', myconstructor) + +trusted_yaml = "mag: !hello world" + +print("============ Loading trusted YAML") + +data = yaml.load(trusted_yaml, Loader=yaml.Loader) +print(data) + + +# somewhere else in your application +untrusted_yaml = "mag: !hello evil" + +print("============ Loading untrusted YAML") +try: + safe_data = yaml.safe_load(untrusted_yaml) + print(safe_data) +except yaml.constructor.ConstructorError as err: + print("Could not load unsafe_yaml:") + print(format(err)) + + From 042c037719e428dcf06e5e9d0e488cde1c5fe4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tina=20M=C3=BCller?= Date: Sun, 1 Jul 2018 21:34:32 +0200 Subject: [PATCH 4/5] Rename Danger to Python --- lib/yaml/cyaml.py | 8 ++++---- lib/yaml/dumper.py | 4 ++-- lib/yaml/loader.py | 4 ++-- tests/lib/test_constructor.py | 4 ++-- tests/lib/test_recursive.py | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/yaml/cyaml.py b/lib/yaml/cyaml.py index 7e423c51..af271865 100644 --- a/lib/yaml/cyaml.py +++ b/lib/yaml/cyaml.py @@ -1,6 +1,6 @@ -__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader', 'CDangerLoader', - 'CBaseDumper', 'CSafeDumper', 'CDumper', 'CDangerDumper'] +__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader', 'CPythonLoader', + 'CBaseDumper', 'CSafeDumper', 'CDumper', 'CPythonDumper'] from _yaml import CParser, CEmitter @@ -32,7 +32,7 @@ def __init__(self, stream): SafeConstructor.__init__(self) Resolver.__init__(self) -class CDangerLoader(CParser, Constructor, Resolver): +class CPythonLoader(CParser, Constructor, Resolver): def __init__(self, stream): CParser.__init__(self, stream) @@ -74,7 +74,7 @@ def __init__(self, stream, Resolver.__init__(self) CSafeDumper = CDumper -class CDangerDumper(CEmitter, Serializer, Representer, Resolver): +class CPythonDumper(CEmitter, Serializer, Representer, Resolver): def __init__(self, stream, default_style=None, default_flow_style=None, diff --git a/lib/yaml/dumper.py b/lib/yaml/dumper.py index 22fd9271..1095a3de 100644 --- a/lib/yaml/dumper.py +++ b/lib/yaml/dumper.py @@ -1,5 +1,5 @@ -__all__ = ['BaseDumper', 'SafeDumper', 'Dumper', 'DangerDumper'] +__all__ = ['BaseDumper', 'SafeDumper', 'Dumper', 'PythonDumper'] from emitter import * from serializer import * @@ -43,7 +43,7 @@ def __init__(self, stream, Resolver.__init__(self) SafeDumper = Dumper -class DangerDumper(Emitter, Serializer, Representer, Resolver): +class PythonDumper(Emitter, Serializer, Representer, Resolver): def __init__(self, stream, default_style=None, default_flow_style=None, diff --git a/lib/yaml/loader.py b/lib/yaml/loader.py index c17ca6d3..f3ad023c 100644 --- a/lib/yaml/loader.py +++ b/lib/yaml/loader.py @@ -1,5 +1,5 @@ -__all__ = ['BaseLoader', 'SafeLoader', 'Loader', 'DangerLoader'] +__all__ = ['BaseLoader', 'SafeLoader', 'Loader', 'PythonLoader'] from reader import * from scanner import * @@ -38,7 +38,7 @@ def __init__(self, stream): SafeConstructor.__init__(self) Resolver.__init__(self) -class DangerLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): +class PythonLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): def __init__(self, stream): Reader.__init__(self, stream) diff --git a/tests/lib/test_constructor.py b/tests/lib/test_constructor.py index 12d53913..6a03f7a6 100644 --- a/tests/lib/test_constructor.py +++ b/tests/lib/test_constructor.py @@ -19,9 +19,9 @@ def _make_objects(): NewArgs, NewArgsWithState, Reduce, ReduceWithState, MyInt, MyList, MyDict, \ FixedOffset, today, execute - class MyLoader(yaml.DangerLoader): + class MyLoader(yaml.PythonLoader): pass - class MyDumper(yaml.DangerDumper): + class MyDumper(yaml.PythonDumper): pass class MyTestClass1: diff --git a/tests/lib/test_recursive.py b/tests/lib/test_recursive.py index ccc9f345..ae5076e3 100644 --- a/tests/lib/test_recursive.py +++ b/tests/lib/test_recursive.py @@ -29,9 +29,9 @@ def test_recursive(recursive_filename, verbose=False): value2 = None output2 = None try: - output1 = yaml.dump(value1, Dumper=yaml.DangerDumper) - value2 = yaml.load(output1, Loader=yaml.DangerLoader) - output2 = yaml.dump(value2, Dumper=yaml.DangerDumper) + output1 = yaml.dump(value1, Dumper=yaml.PythonDumper) + value2 = yaml.load(output1, Loader=yaml.PythonLoader) + output2 = yaml.dump(value2, Dumper=yaml.PythonDumper) assert output1 == output2, (output1, output2) finally: if verbose: From e64edb73a83930ec0c5d8222d040f0f6d7f52c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tina=20M=C3=BCller?= Date: Mon, 2 Jul 2018 00:43:13 +0200 Subject: [PATCH 5/5] Fix typo --- examples/safe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/safe.py b/examples/safe.py index 7ff87332..9cf74534 100644 --- a/examples/safe.py +++ b/examples/safe.py @@ -8,7 +8,7 @@ def myconstructor(loader, node): yaml.Loader.add_constructor(u'!hello', myconstructor) -trusted_yaml = "mag: !hello world" +trusted_yaml = "msg: !hello world" print("============ Loading trusted YAML") @@ -17,7 +17,7 @@ def myconstructor(loader, node): # somewhere else in your application -untrusted_yaml = "mag: !hello evil" +untrusted_yaml = "msg: !hello evil" print("============ Loading untrusted YAML") try: