diff --git a/scripts/modules/rna_xml.py b/scripts/modules/rna_xml.py index 41d3747c43d..6c30a8d8a55 100644 --- a/scripts/modules/rna_xml.py +++ b/scripts/modules/rna_xml.py @@ -229,14 +229,33 @@ def rna2xml( fw("{:s}\n".format(root_ident, root_node)) +# NOTE(@ideasman42): regarding `secure_types`. +# This is a safe guard when loading an untrusted XML to prevent any possibility of the XML +# paths "escaping" the intended data types, potentially writing into unexpected settings. +# This is done because the XML itself defines the attributes which are recursed into, +# there is a possibility the XML recurse into data that isn't logically owned by "root", +# out of the theme and into user preferences for e.g. which could change trust settings +# even executing code. +# +# At the time of writing it seems this is not possible with themes (the main user of this functionality), +# however this could become possible in the future through additional RNA properties and it wouldn't be +# obvious an exploit existed. +# +# In short, it's safest for users of this API to restrict types when loading untrusted XML. + def xml2rna( root_xml, *, root_rna=None, # must be set + secure_types=None, # `Optional[Set[str]]` ): def xml2rna_node(xml_node, value): # print("evaluating:", xml_node.nodeName) + if (secure_types is not None) and (xml_node.nodeName not in secure_types): + print("Loading the XML with type restrictions, skipping \"{:s}\"".format(xml_node.nodeName)) + return + # --------------------------------------------------------------------- # Simple attributes @@ -354,7 +373,12 @@ def _get_context_val(context, path): return value -def xml_file_run(context, filepath, rna_map): +def xml_file_run( + context, + filepath, + rna_map, + secure_types=None, # `Optional[Set[str]]` +): import xml.dom.minidom xml_nodes = xml.dom.minidom.parse(filepath) @@ -370,7 +394,7 @@ def xml_file_run(context, filepath, rna_map): if value is not Ellipsis and value is not None: # print(" loading XML: {!r} -> {!r}".format(filepath, rna_path)) - xml2rna(xml_node, root_rna=value) + xml2rna(xml_node, root_rna=value, secure_types=secure_types) def xml_file_write(context, filepath, rna_map, *, skip_typemap=None): diff --git a/scripts/startup/bl_operators/presets.py b/scripts/startup/bl_operators/presets.py index 013a8377092..e19c1b3f4ab 100644 --- a/scripts/startup/bl_operators/presets.py +++ b/scripts/startup/bl_operators/presets.py @@ -274,7 +274,10 @@ class ExecutePreset(Operator): elif ext == ".xml": import rna_xml - rna_xml.xml_file_run(context, filepath, preset_class.preset_xml_map) + preset_xml_map = preset_class.preset_xml_map + preset_xml_secure_types = getattr(preset_class, "preset_xml_secure_types", None) + + rna_xml.xml_file_run(context, filepath, preset_xml_map, secure_types=preset_xml_secure_types) _call_preset_cb(getattr(preset_class, "post_cb", None), context, filepath) diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 5c197279b50..d84bdc05f9d 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -931,6 +931,43 @@ class USERPREF_MT_interface_theme_presets(Menu): ("preferences.themes[0]", "Theme"), ("preferences.ui_styles[0]", "ThemeStyle"), ) + # Prevent untrusted XML files "escaping" from these types. + preset_xml_secure_types = { + "Theme", + "ThemeAssetShelf", + "ThemeBoneColorSet", + "ThemeClipEditor", + "ThemeCollectionColor", + "ThemeConsole", + "ThemeDopeSheet", + "ThemeFileBrowser", + "ThemeFontStyle", + "ThemeGradientColors", + "ThemeGraphEditor", + "ThemeImageEditor", + "ThemeInfo", + "ThemeNLAEditor", + "ThemeNodeEditor", + "ThemeOutliner", + "ThemePanelColors", + "ThemePreferences", + "ThemeProperties", + "ThemeSequenceEditor", + "ThemeSpaceGeneric", + "ThemeSpaceGradient", + "ThemeSpaceListGeneric", + "ThemeSpreadsheet", + "ThemeStatusBar", + "ThemeStripColor", + "ThemeStyle", + "ThemeTextEditor", + "ThemeTopBar", + "ThemeUserInterface", + "ThemeView3D", + "ThemeWidgetColors", + "ThemeWidgetStateColors", + } + draw = Menu.draw_preset @staticmethod