Register global import hooks in python
Question:
I want to add libraries access control to my installation of python and I wanted to know if there is some way to hook into the import system, so that I can run a script to check if a python program is allowed to import libraries, to block untrusted modules from importing dangerous native modules that could leak information on my system, like os
.
While researching by myself, I found out about PEP 302, which sounds like what I am looking for, but I couldn’t find how to register those hooks installation-wide.
Would someone be able to tell me if there is a way in python to add such an import hook to all imports on the system rather than only on the currently executing program?
Answers:
You can change the import of modules by implementing you own custom import loader object. A starting point in the documentation can be found here: https://docs.python.org/3/library/importlib.html
What you need to do is to create a loader that will act on the packages you want to check on, and then either load them, or raise a desired exception. In the case of modules what are not in your access control list, you should return None, this makes the import machinery load them normally. I have create a minimal example of this type of functionality that you can start from and extend to build your desired functionality.
import sys
import importlib
class ImportInterceptor(importlib.abc.Loader):
def __init__(self, package_permissions):
self.package_permissions = package_permissions
def find_module(self, fullname, path=None):
if fullname in self.package_permissions:
if self.package_permissions[fullname]:
return self
else:
raise ImportError("Package import was not allowed")
def load_module(self, fullname):
sys.meta_path = [x for x in sys.meta_path[1:] if x is not self]
module = importlib.import_module(fullname)
sys.meta_path = [self] + sys.meta_path
return module
if not hasattr(sys,'frozen'):
sys.meta_path = [ImportInterceptor({'textwrap': True, 'Pathlib': False})] + sys.meta_path
import textwrap
print(textwrap.dedent(' test'))
# Works fine
from pathlib import Path
# Raises exception
Note that the loader removes itself from sys.meta_path when loading the package. This is to avoid an infinite loop where it keeps calling itself every time it tries to load the module “for real”.
I want to add libraries access control to my installation of python and I wanted to know if there is some way to hook into the import system, so that I can run a script to check if a python program is allowed to import libraries, to block untrusted modules from importing dangerous native modules that could leak information on my system, like os
.
While researching by myself, I found out about PEP 302, which sounds like what I am looking for, but I couldn’t find how to register those hooks installation-wide.
Would someone be able to tell me if there is a way in python to add such an import hook to all imports on the system rather than only on the currently executing program?
You can change the import of modules by implementing you own custom import loader object. A starting point in the documentation can be found here: https://docs.python.org/3/library/importlib.html
What you need to do is to create a loader that will act on the packages you want to check on, and then either load them, or raise a desired exception. In the case of modules what are not in your access control list, you should return None, this makes the import machinery load them normally. I have create a minimal example of this type of functionality that you can start from and extend to build your desired functionality.
import sys
import importlib
class ImportInterceptor(importlib.abc.Loader):
def __init__(self, package_permissions):
self.package_permissions = package_permissions
def find_module(self, fullname, path=None):
if fullname in self.package_permissions:
if self.package_permissions[fullname]:
return self
else:
raise ImportError("Package import was not allowed")
def load_module(self, fullname):
sys.meta_path = [x for x in sys.meta_path[1:] if x is not self]
module = importlib.import_module(fullname)
sys.meta_path = [self] + sys.meta_path
return module
if not hasattr(sys,'frozen'):
sys.meta_path = [ImportInterceptor({'textwrap': True, 'Pathlib': False})] + sys.meta_path
import textwrap
print(textwrap.dedent(' test'))
# Works fine
from pathlib import Path
# Raises exception
Note that the loader removes itself from sys.meta_path when loading the package. This is to avoid an infinite loop where it keeps calling itself every time it tries to load the module “for real”.