[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/apidoc/c * Fixed security hole by disabling automatic import of paths when a

Stephan Richter srichter at cosmos.phy.tufts.edu
Sat Jun 18 09:25:59 EDT 2005


Log message for revision 30838:
  * Fixed security hole by disabling automatic import of paths when a
    module is not available in ``sys.modules`` by default. It can be
    activated using::
    
      <apidoc:moduleImport allow="true" /> 
  
  

Changed:
  U   Zope3/trunk/src/zope/app/apidoc/classregistry.py
  U   Zope3/trunk/src/zope/app/apidoc/classregistry.txt
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/directives.txt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/meta.zcml
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/metadirectives.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py

-=-
Modified: Zope3/trunk/src/zope/app/apidoc/classregistry.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classregistry.py	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/classregistry.py	2005-06-18 13:25:59 UTC (rev 30838)
@@ -16,6 +16,9 @@
 $Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
 """
 __docformat__ = 'restructuredtext'
+
+__import_unknown_modules__ = False
+
 import sys
 
 from zope.app import zapi
@@ -52,7 +55,7 @@
 def safe_import(path, default=None):
     """Import a given path as efficiently as possible and without failure."""
     module = sys.modules.get(path, default)
-    if module is default:
+    if module is default and __import_unknown_modules__:
         try:
             module = __import__(path, {}, {}, ('*',))
         except ImportError:

Modified: Zope3/trunk/src/zope/app/apidoc/classregistry.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classregistry.txt	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/classregistry.txt	2005-06-18 13:25:59 UTC (rev 30838)
@@ -4,8 +4,8 @@
 
 This little registry allows us to quickly query a complete list of classes
 that are defined and used by Zope 3. The prime feature of the class is the
-'getClassesThatImplement(iface)' method that returns all classes that
-implement the passed interface. Another method, 'getSubclassesOf(klass)'
+``getClassesThatImplement(iface)`` method that returns all classes that
+implement the passed interface. Another method, ``getSubclassesOf(klass)``
 returns all registered subclassess of the given class.
 
 The class registry, subclassing the dictionary type, can be instantiated like
@@ -96,14 +96,17 @@
 Safe Imports
 ------------
 
-Using the `safe_import()` we can quickly look up modules by minimizing import
-calls.
+Using the ``safe_import()`` we can quickly look up modules by minimizing
+import calls.
 
+  >>> from zope.app.apidoc import classregistry
   >>> from zope.app.apidoc.classregistry import safe_import
 
-First we try to find the path in 'sys.modules', since this lookup is much
+First we try to find the path in ``sys.modules``, since this lookup is much
 more efficient than importing it. If it was not found, we go back and try
-to import the path. If that also fails, we return the 'default' value.
+to import the path. For security reasons, importing new modules is disabled by
+default, unless the global ``__import_unknown_modules__`` variable is set to
+true. If that also fails, we return the `default` value.
 
 Here are some examples::
 
@@ -126,12 +129,23 @@
   >>> f.write('# dummy module\n')
   >>> f.close()
 
-The temporary module is not already imported, but will be once
-we've called safe_import():
+The temporary module is not already imported:
 
   >>> module_name = 'zope.app.apidoc.testmodule'
   >>> module_name in sys.modules
   False
+
+When we try ``safe_import()`` now, we will still get the `default` value,
+because importing new modules is disabled by default:
+
+  >>> safe_import(module_name) is None
+  True
+
+But once we activate the ``__import_unknown_modules__`` hook, the module
+should be imported:
+
+  >>> classregistry.__import_unknown_modules__ = True
+
   >>> safe_import(module_name).__name__ == module_name
   True
   >>> module_name in sys.modules
@@ -145,4 +159,9 @@
   >>> if os.path.exists(filename + 'o'):
   ...     os.unlink(filename + 'o')
 
-  >>> del sys.modules['zope.app.apidoc.testmodule']
\ No newline at end of file
+  >>> del sys.modules['zope.app.apidoc.testmodule']
+
+We also need to play nice concerning variables and have to reset the module
+globals:
+
+  >>> classregistry.__import_unknown_modules__ = False
\ No newline at end of file

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt	2005-06-18 13:25:59 UTC (rev 30838)
@@ -1,34 +0,0 @@
-=================================
-The `apidoc:rootModule` Directive
-=================================
-
-The `rootModule` directive allows you to register a third party Python package
-with apidoc's code browser. 
-
-Before we can register a new root module, we need to load the
-metaconfiguration:
-
-  >>> from zope.configuration import xmlconfig
-  >>> import zope.app.apidoc.codemodule
-  >>> context = xmlconfig.file('meta.zcml', zope.app.apidoc.codemodule)
-
-Now we can run the directive. First, let's make sure that no root modules have
-been registered yet:
-
-  >>> from zope.app import zapi
-  >>> from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
-  >>> list(zapi.getUtilitiesFor(IAPIDocRootModule))
-  []
-
-Now run the registration code:
-
-  >>> context = xmlconfig.string('''
-  ...     <configure
-  ...         xmlns='http://namespaces.zope.org/apidoc'>
-  ...       <rootModule module="zope" />
-  ...     </configure>''', context)
-
-and the root module is available:
-
-  >>> list(zapi.getUtilitiesFor(IAPIDocRootModule))
-  [(u'zope', 'zope')]

Copied: Zope3/trunk/src/zope/app/apidoc/codemodule/directives.txt (from rev 30837, Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt)
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/directives.txt	2005-06-18 13:25:59 UTC (rev 30838)
@@ -0,0 +1,78 @@
+========================================
+Code Module specific `apidoc` Directives 
+========================================
+
+The `apidoc:rootModule` Directive
+---------------------------------
+
+The `rootModule` directive allows you to register a third party Python package
+with apidoc's code browser. 
+
+Before we can register a new root module, we need to load the
+metaconfiguration:
+
+  >>> from zope.configuration import xmlconfig
+  >>> import zope.app.apidoc.codemodule
+  >>> context = xmlconfig.file('meta.zcml', zope.app.apidoc.codemodule)
+
+Now we can run the directive. First, let's make sure that no root modules have
+been registered yet:
+
+  >>> from zope.app import zapi
+  >>> from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
+  >>> list(zapi.getUtilitiesFor(IAPIDocRootModule))
+  []
+
+Now run the registration code:
+
+  >>> context = xmlconfig.string('''
+  ...     <configure
+  ...         xmlns="http://namespaces.zope.org/apidoc">
+  ...       <rootModule module="zope" />
+  ...     </configure>''', context)
+
+and the root module is available:
+
+  >>> list(zapi.getUtilitiesFor(IAPIDocRootModule))
+  [(u'zope', 'zope')]
+
+
+The `apidoc:importModule` Directive
+-----------------------------------
+
+The `importModule` directive allows you to set the
+``__import_unknown_modules__`` flag of the class registry. When this flag is
+set to false, paths will only be looked up in ``sys.modules``. When set true,
+and the ``sus.modules`` lookup fails, the import function of the class
+registry tries to import the path. The hook was provided for security reasons,
+since uncontrolled importing of modules in a running application is considered
+a security hole.
+
+By default the flag is set to false:
+
+  >>> from zope.app.apidoc import classregistry
+  >>> classregistry.__import_unknown_modules__
+  False
+
+We can now use the directive to set it to true:
+
+  >>> context = xmlconfig.string('''
+  ...     <configure
+  ...         xmlns="http://namespaces.zope.org/apidoc">
+  ...       <moduleImport allow="true" />
+  ...     </configure>''', context)
+
+  >>> classregistry.__import_unknown_modules__
+  True
+
+We can also set it back to false of course:
+
+  >>> context = xmlconfig.string('''
+  ...     <configure
+  ...         xmlns="http://namespaces.zope.org/apidoc">
+  ...       <moduleImport allow="false" />
+  ...     </configure>''', context)
+
+  >>> classregistry.__import_unknown_modules__
+  False
+

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/meta.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/meta.zcml	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/meta.zcml	2005-06-18 13:25:59 UTC (rev 30838)
@@ -6,6 +6,12 @@
   <meta:directives namespace="http://namespaces.zope.org/apidoc">
 
     <meta:directive
+        name="moduleImport"
+        schema=".metadirectives.IModuleImport"
+        handler=".metaconfigure.moduleImport"
+        />
+
+    <meta:directive
         name="rootModule"
         schema=".metadirectives.IRootModule"
         handler=".metaconfigure.rootModule"

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py	2005-06-18 13:25:59 UTC (rev 30838)
@@ -18,11 +18,25 @@
 __docformat__ = 'restructuredtext'
 from zope.interface import implements
 from zope.app.component.metaconfigure import utility
+
+from zope.app.apidoc import classregistry
 from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
 
+
 class RootModule(str):
     implements(IAPIDocRootModule)
 
 def rootModule(_context, module):
     """Register a new module as a root module for the class browser."""
     utility(_context, IAPIDocRootModule, RootModule(module), name=module)
+
+
+def setModuleImport(flag):
+    classregistry.__import_unknown_modules__ = flag
+
+def moduleImport(_context, allow):
+    """Set the __import_unknown_modules__ flag"""
+    return _context.action(
+        ('apidoc', '__import_unknown_modules__'),
+        setModuleImport,
+        (allow, ))

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/metadirectives.py	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/metadirectives.py	2005-06-18 13:25:59 UTC (rev 30838)
@@ -16,15 +16,26 @@
 $Id: metadirectives.py 26613 2004-07-18 21:50:40Z srichter $
 """
 __docformat__ = 'restructuredtext'
-from zope.interface import Interface
-from zope.schema import TextLine
+import zope.interface
+import zope.schema
 
-class IRootModule(Interface):
+class IRootModule(zope.interface.Interface):
     """Declares a new root module to be available for the class documentation
     module."""
 
-    module = TextLine(
+    module = zope.schema.TextLine(
         title=u"Root Module Name",
         description=u"This is the Python path of the new root module.",
         required=True
         )
+
+class IModuleImport(zope.interface.Interface):
+    """Set a flag whether new modules can be imported to the class registry or
+       not."""
+
+    allow = zope.schema.Bool(
+        title=u"Allow Importing Modules",
+        description=u"When set to true, new modules will be imported by path.",
+        required=True,
+        default=False
+        )

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py	2005-06-17 23:09:06 UTC (rev 30837)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py	2005-06-18 13:25:59 UTC (rev 30838)
@@ -49,7 +49,7 @@
                              setUp=setUp, tearDown=tearDown,
                              globs={'pprint': doctestunit.pprint},
                              optionflags=doctest.NORMALIZE_WHITESPACE),
-        doctest.DocFileSuite('directive.txt',
+        doctest.DocFileSuite('directives.txt',
                              setUp=placelesssetup.setUp,
                              tearDown=placelesssetup.tearDown),
         ))



More information about the Zope3-Checkins mailing list