diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..69ec2cdb6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs +# For more information about the EditorConfig project, please visit http://editorconfig.org + +# Top-most EditorConfig file +root = true + +# Ensure there is a final newline at the end of the file +[*] +insert_final_newline = true \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..5ba836751 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,38 @@ +name: Linting +on: [pull_request] +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install + + - name: Run Pylint + run: | + poetry run pylint --fail-under=9 --rcfile=.pylintrc aikido_firewall/ + pylint_exit_code=$? + if [ $pylint_exit_code -ne 0 ]; then + echo "Pylint check failed. Please fix the issues." + exit 1 + fi + + - name: Run Black Check + run: | + poetry run black --check --diff aikido_firewall/ + black_exit_code=$? + if [ $black_exit_code -ne 0 ]; then + echo "Black check failed. Please run 'black .' to format the code." + exit 1 + fi diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..f59adbb14 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,7 @@ +[MASTER] +# Disable the unused-argument warning +disable = unused-argument + +[MESSAGES CONTROL] +# Disable the no-member warning [Currently enabled] +#disable = no-member diff --git a/Makefile b/Makefile index 28018b829..a3505b23e 100644 --- a/Makefile +++ b/Makefile @@ -4,4 +4,9 @@ build: .PHONY: clean clean: - poetry env remove python \ No newline at end of file + poetry env remove python + +.PHONY: lint +lint: + poetry run black aikido_firewall/ + poetry run pylint aikido_firewall/ diff --git a/aikido_firewall/__init__.py b/aikido_firewall/__init__.py index 8a0074157..1dfe0b327 100644 --- a/aikido_firewall/__init__.py +++ b/aikido_firewall/__init__.py @@ -1,6 +1,10 @@ +""" +Aggregates from the different modules +""" + # Import sources import aikido_firewall.sources.django import aikido_firewall.sources.flask # Import middleware -import aikido_firewall.middleware.django \ No newline at end of file +import aikido_firewall.middleware.django diff --git a/aikido_firewall/middleware/django.py b/aikido_firewall/middleware/django.py index 87f7fa090..d855848aa 100644 --- a/aikido_firewall/middleware/django.py +++ b/aikido_firewall/middleware/django.py @@ -1,5 +1,16 @@ +""" +Django WSGI Aikido Middleware +uses headers, body, etc. as sources +""" + import logging + + class AikidoMiddleware: + """ + Same as docstring above + """ + def __init__(self, get_response): self.get_response = get_response @@ -8,7 +19,13 @@ def __call__(self, request, *args, **kwargs): return self.get_response(request) def process_exception(self, request, exception): + """ + Do something when an exception occurs whilst django is processing a request + """ logging.critical("[AIK] Aikido middleware : exception") - + def process_request(self, request): + """ + executed during the request phase of the Django request-response cycle. + """ logging.critical("[AIK] Aikido middleware : request") diff --git a/aikido_firewall/sources/django.py b/aikido_firewall/sources/django.py index 1d470340f..e6d89ad1c 100644 --- a/aikido_firewall/sources/django.py +++ b/aikido_firewall/sources/django.py @@ -1,16 +1,23 @@ +""" +Django source module, intercepts django import and adds Aikido middleware +""" + import importhook -import copy -from importlib.metadata import version AIKIDO_MIDDLEWARE_ADDR = "aikido_firewall.middleware.django.AikidoMiddleware" -# Hook 'n wrap on `django.conf` -# Our goal here is to wrap the settings object and add our middleware into the list -@importhook.on_import('django.conf') + +@importhook.on_import("django.conf") def on_django_import(django): + """ + Hook 'n wrap on `django.conf` + Our goal here is to wrap the settings object and add our middleware into the list + Returns : Modified django.conf object + """ modified_django = importhook.copy_module(django) new_middleware_array = [AIKIDO_MIDDLEWARE_ADDR] + django.settings.MIDDLEWARE + # pylint: disable=no-member setattr(modified_django.settings, "MIDDLEWARE", new_middleware_array) print("[AIK] Modified Django") - return modified_django \ No newline at end of file + return modified_django diff --git a/aikido_firewall/sources/flask.py b/aikido_firewall/sources/flask.py index a565f97cd..0fa2b7d67 100644 --- a/aikido_firewall/sources/flask.py +++ b/aikido_firewall/sources/flask.py @@ -1,9 +1,18 @@ -import importhook +""" +Flask source module, intercepts flask import and adds Aikido middleware +""" + import copy from importlib.metadata import version import logging +import importhook + + +class AikidoMiddleware: # pylint: disable=too-few-public-methods + """ + Aikido WSGI Middleware | uses headers, body, etc. as sources + """ -class AikidoMiddleware(object): def __init__(self, app): self.app = app @@ -13,19 +22,24 @@ def __call__(self, environ, start_response): return response -# Hook 'n wrap on `flask.app` -# Our goal is to wrap the __init__ function of the "Flask" class, so we can insert our middleware -@importhook.on_import('flask.app') +@importhook.on_import("flask.app") def on_flask_import(flask): + """ + Hook 'n wrap on `flask.app` + Our goal is to wrap the __init__ function of the "Flask" class, so we can insert our middleware + Returns : Modified flask.app object + """ modified_flask = importhook.copy_module(flask) prev_flask_init = copy.deepcopy(flask.Flask.__init__) + def aikido_flask_init(_self, *args, **kwargs): prev_flask_init(_self, *args, **kwargs) print("[AIK] Flask version : ", version("flask")) _self.wsgi_app = AikidoMiddleware(_self.wsgi_app) print(_self) - + + # pylint: disable=no-member setattr(modified_flask.Flask, "__init__", aikido_flask_init) print("[AIK] Modified flask") - return modified_flask \ No newline at end of file + return modified_flask diff --git a/docs/contributing/python.md b/docs/contributing/python.md index e4fa60991..8af4d823b 100644 --- a/docs/contributing/python.md +++ b/docs/contributing/python.md @@ -1,10 +1,27 @@ -# Python configuration +# Poetry -To install and keep track of packages we use "pipenv", so installing packages goes as follows : +To run scripts, run pylint, etc. enter the virtualenv using : ```bash -pipenv shell +poetry shell ``` -And from now on you can install all packages with : + +When adding new packages, add them using : +```bash +poetry add +``` + +# Building +If you want to build this python package you can execute : +```bash +make build +``` +When you're done or when you want to clean up use : +```bash +make clean +``` + +# Linting +We use `black` and `pylint`. To run these tools use : +```bash +make lint ``` -pipenv install -``` \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 1a34ca24f..4cefad54a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,100 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "astroid" +version = "3.2.3" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.3-py3-none-any.whl", hash = "sha256:3eae9ea67c11c858cdd2c91337d2e816bd019ac897ca07d7b346ac10105fceb3"}, + {file = "astroid-3.2.3.tar.gz", hash = "sha256:7099b5a60985529d8d46858befa103b82d0d05a5a5e8b816b5303ed96075e1d9"}, +] + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + [[package]] name = "importhook" version = "1.0.9" @@ -11,7 +106,116 @@ files = [ {file = "importhook-1.0.9.tar.gz", hash = "sha256:635ad367225fa022221025545c167b09d09ada671821b6e587df0d5ae4bf0aab"}, ] +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pylint" +version = "3.2.5" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.5-py3-none-any.whl", hash = "sha256:32cd6c042b5004b8e857d727708720c54a676d1e22917cf1a2df9b4d4868abd6"}, + {file = "pylint-3.2.5.tar.gz", hash = "sha256:e9b7171e242dcc6ebd0aaa7540481d1a72860748a0a7816b8fe6cf6c80a6fe7e"}, +] + +[package.dependencies] +astroid = ">=3.2.2,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "tomlkit" +version = "0.13.0" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, + {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, +] + [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "646bb6ea3da065aa5a4e3683f8cdb1359e22726c0d06a109bb19a2d3f383c661" +content-hash = "9eb7e93a0a3575f48f3e4590a1cd7f0b25608fc0e0b632d36e012a2742e95c10" diff --git a/pyproject.toml b/pyproject.toml index 8efb97838..88739bb6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,14 @@ readme = "README.md" python = "^3.12" importhook = "^1.0.9" +[tool.poetry.group.dev.dependencies] +black = "^24.4.2" +pylint = "^3.2.5" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 88 +target-version = ['py38']