codewars / docs

The Codewars Docs :construction: WIP
https://docs.codewars.com
MIT License
57 stars 201 forks source link

Describe "module forbidder" #199

Open hobovsky opened 3 years ago

hobovsky commented 3 years ago

A snippet with module forbidder.

These kata implements quite hard-to-bypass module forbidding in python:

XRFXLP commented 3 years ago

Copy/pasted from one of the preloaded:


                       PURPOSE:
            This code blocks all attempts to import one of the targeted modules, in any of the
            following ways manners (here, for numpy):

                    import numpy
                    __import__("numpy")
                    import numpy.linalg
                    from numpy import array

            Calling the import function from globals, locals or even __builtins__ are blocked
            the very same way since they all refer to the very same function.

        USAGE:

            Update the extension of the list 'toForbid', at the end of the preloaded part with 
            the names of the modules you wanna forbid.

        WARNING:

            * Some packages, when imported, have dependencies that can contain one or many 
              modules that are forbidden.
              The typical problem is that 'random' cannot be imported anymore if 'os' or 'sys'
              are forbidden. Numpy needs 'sys' too, for example, and so on...

              That leads to another problem: you may forbid some legit imports too.

              See "SUBSEQUENT IMPORTS" and "AUTHORIZING USER IMPORTS" sections to handle
              those problems.

            * If you wanna forbid the 're' module, you'll have to add its name after the 'sys' 
              module at this line:

                  for name in modulesToKeep + ('sys', 're'):
                      ...

              Otherwise the user will have access to it (because it's used in the module_forbidder)

        EFFICIENCY REQUIREMENTS:

            - To make it useful, some modules have to be forbidden by default. They are listed 
              in the first declaration of the tuple 'toForbid', (see lower). 

            - To forbid retro-engineering of the module_forbidder, it's reference in the global 
              scope is deleted.
              Note that the 'inspect' module isn't reachable anymore because its import relies 
              on some of the modules forbiden by default (like os and/or sys)

            - Finally, open, exec and eval functions are "Nonified" to forbid some other ways to 
              cheat.

            Though, that makes further imports completely impossible after the forbidder 
            has been setup: see the "SUBSEQUENT IMPORTS" part, about the import needed in the 
            test cases part.

        SUBSEQUENT IMPORTS:            (importing modules for the test suite)

            Because of the requirements to keep the forbidder working, imports from the test suite aren't
            possible anymore. To get those modules in there, without letting to the user the possiblity to
            mess with them, you have to go through the "extract" process:

                1. In the 'module_forbidder' function, import all the module you need. To do so, use the
                   'modulesToKeep' tuple, putting in there the names of the modules as strings.
                   The imports are scope specific, and since the sys, gc or whatever modules aren't accesible 
                   anymore, the user cannot have acces to them.
                   Those module names will automatically be added to the restricted names (because even if the
                   imports are scope dependent the user could have acces to them trying a regular import, since
                   python imports only one single time a module (unless 'importlib.reload' or equivalent is used)
                   and the user would actually only create a new alias for the alredy stored module).

                2. Define the 'checker' variable, as "extract.(some random char sequence here)" to be able to 
                   access the modules you loaded before. 
                   Note: the char sequence has to be consistent with the syntaxe of the imports => like r'\w+'

                3. from the test suite, get back the different modules with the "extract" fake import and then
                   do your stuff with them.

                Example:   - In the module forbidder:

                                checker       = 'extract._HjsloidfgskhS543668DGHZ'
                                modulesToKeep = ('random',)

                           - Then in the test suite:

                                (random,) =  __import__('extract._HjsloidfgskhS543668DGHZ')
                                randint   = random.randint

                Reminder: Do NEVER import random right from the preloaded (root) part, unless you're ok with a 
                          user messing with its seed... x)

        AUTHORIZING USER IMPORTS:    (== "don't make the user hate you"... x) )

            If you have to allow for the user some modules that have dependencies on forbidden modules, you 
            just have to make those imports yourself at the beginning of the preloaded part.

            Just do themin their simplest form, like 'import numpy', and it will handle any aliasing the 
            user could use (like 'import numpy as np').

            Note: the classic modules still can be imported the usual way by the user:
                        heapq, itertools, collections, functools, operator, re, ...

        COMPATIBILITY NOTES:
            Warning if higher versions are available: 3.6.4, for example, needs the following change:
                globals()['__builtins__'].__import__ =...
            instead of:
                __builtins__['__import__'] = ...

        STRATEGY NOTE (archive):
            A first verison was using a "switch", to activate/deactivate the import forbider, so that the author 
            can import stuff in the test suite (which is loaded after the code of the user), but since the addition
            of the resetting of the open and exec functions, some modules (most of them...) cannot be imported 
            anymore, even if the original import function is used.
            That leads to te use of the "extract" fake import.
Blind4Basics commented 3 years ago

it's not up to date...

the thing about the pwd to get the random module back in the tests doesn't work properly (if not at all...)

XRFXLP commented 3 years ago

the thing about the pwd to get the random module back in the tests doesn't work properly

Can you please elaborate?