PyLint and Django-Lint for Python Static Analysis

I used PyLint and Django-Lint on an internal engagement recently and wanted to show how simple they are to use.

PyLint and Django-Lint - Introduction

Django-Lint Installation

While testing the Django application, I decided to try out django-lint for some static analysis.

First, I installed the django-lint module using pip.

doyler@mbp:~/Documents/demo_tool$ pip install django-lint
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting django-lint

Next, I tried to run the tool against the application, but I was in the wrong folder.

doyler@mbp:~/Documents/demo_tool/source$ django-lint demo-tool-develop
Usage:  django-lint [options] target

    Django Lint is a tool that statically analyses Django projects and
    applications, checking for programming errors and bad code smells. For
    example, it reports nullable "CharField" fields, as well as reporting for
    unspecified options in settings.py.

    The `target` argument is mandatory and can specify either a directory
    containing a Django project, a single application or a single file.

django-lint: error: The specified target ('/Users/doyler/Documents/demo_tool/source/demo-tool-develop') does not appear to be part of a Django application

When I went to the correct folder, I learned that I was missing the django module.

doyler@mbp:~/Documents/demo_tool/source/demo-tool-develop$ django-lint LearningDjango
E: Cannot import `django' module, exiting..

Finally, I installed Django, and was ready to start.

doyler@mbp:~/Documents/demo_tool/source/demo-tool-develop$ pip install django
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting django
  Downloading https://files.pythonhosted.org/packages/49/49/178daa8725d29c475216259eb19e90b2aa0b8c0431af8c7e9b490ae6481d/Django-1.11.29-py2.py3-none-any.whl (6.9MB)

Django-Lint Usage

With Django and django-lint installed, it was time to see if I could find any vulnerabilities using static analysis.

First, I ran the application against my demo source, but received an error about a missing __init__.py file.

doyler@mbp:~/Documents/demo_tool/source/demo-tool-develop$ django-lint LearningDjango
************* Module LearningDjango
F:  1,0: error while code parsing: Unable to load file '/Users/doyler/Documents/demo_tool/source/demo-tool-develop/LearningDjango/__init__.py' ([Errno 2] No such file or directory: '/Users/doyler/Documents/demo_tool/source/demo-tool-develop/LearningDjango/__init__.py')

After finally going to the correct app directory, I was able to successfully run django-lint! Unfortunately, there was nothing of note, and I just had a few errors and style issues that I could fix.

doyler@mbp:~/Documents/demo_tool/source/demo-tool-develop/LearningDjango/BasicApp$ django-lint -p .
************* Module BasicApp.Demo_Module
C:  1,0: Invalid name "Demo_Module" for type module (should match (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$)
F:  1,0: Unable to import 'pyodbc'

PyLint Installation

With nothing fruitful coming from django-lint during my engagement, I decided to give PyLint a try on my personal repositories.

First, I installed PyLint using pip.

doyler@mbp:~/tools$ pip install pylint
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Requirement already satisfied: pylint in /usr/local/lib/python2.7/site-packages (0.28.0)

This was the only package that I needed to install, so I was ready to try out the application.

PyLint Error and Usage

First, when I ran the application, I only received errors about missing __init__.py files.

doyler@mbp:~/tools/SecurityTools$ pylint ./*
No config file found, using default configuration
************* Module AlexaPortScanner
F:  1,0: error while code parsing: Unable to load file 'AlexaPortScanner/__init__.py' ([Errno 2] No such file or directory: 'AlexaPortScanner/__init__.py')
************* Module AndroidCloseTabs
F:  1,0: error while code parsing: Unable to load file 'AndroidCloseTabs/__init__.py' ([Errno 2] No such file or directory: 'AndroidCloseTabs/__init__.py')

... <snip> ...

************* Module ZipCracker
F:  1,0: error while code parsing: Unable to load file 'ZipCracker/__init__.py' ([Errno 2] No such file or directory: 'ZipCracker/__init__.py')

Next, I ran a quick one-liner to add an init file to every directory that wasn't named '.git'.

doyler@mbp:~/tools/SecurityTools$ find . -type d -not -iwholename '*.git*' -exec touch {}/__init__.py \;

As you can see, this made quite the fun commit to my repository.

Finally, I was able to run the application and check my errors.

As you can see, I received a ton of messages about my repositories, and a few useful errors! I know that this is a wall of text, but I wanted to show some of the common issues that you might run across, as well as leave it here for me to reference in the future.

doyler@mbp:~/tools/SecurityTools$ pylint ./*
No config file found, using default configuration
************* Module AlexaPortScanner
C:  1,0: Missing docstring
************* Module AlexaPortScanner.alexaPortScanner
C:142,0: Line too long (84/80)
C:  1,0: Invalid name "alexaPortScanner" for type module (should match (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$)
C:  1,0: Missing docstring
C:  6,0:build_speechlet_response: Missing docstring

... <snip> ...

************* Module DNSRickroll.dnsRickroll
W: 12,4: Redefining built-in 'hex'
C:  1,0: Invalid name "dnsRickroll" for type module (should match (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$)
C:  1,0: Missing docstring
E:  2,0: No name 'request' in module 'urllib'
E:  2,0: No name 'parse' in module 'urllib'
E:  2,0: No name 'error' in module 'urllib'

... <snip> ...

************* Module ECBPlaintextAttack.ecbServer
W: 18,0: Found indentation with tabs instead of spaces

... <snip> ...

E: 65,14:main: Instance of '_socketobject' has no 'recv' member
E: 73,8:main: Instance of '_socketobject' has no 'send' member
W: 61,17:main: Unused variable 'client_address'

... <snip> ...

************* Module FileIntegrity.fileintegrity
C:  1,0: Missing docstring
E:  9,0: No name 'parse' in module 'urllib'
F:  9,0: Unable to import 'urllib.parse'

... <snip> ...

************* Module IpExpander.ipExpander
C:  1,0: Invalid name "ipExpander" for type module (should match (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$)

... <snip> ...

E:  6,13:isIP: Undefined variable 'IPAddress'
E: 13,18:isCIDR: Undefined variable 'IPNetwork'
E: 19,16:expandCIDR: Undefined variable 'IPNetwork'
E: 30,18:expandRange: Undefined variable 'IPAddress'
E: 39,25:expandRange: Undefined variable 'IPNetwork'
E: 45,20:expandRange: Undefined variable 'IPAddress'
E: 49,16:expandRange: Undefined variable 'iter_iprange'
E: 70,29:main: Undefined variable 'IPAddress'
W:  2,0: Unused import pprint

... <snip> ...


Report
======
729 statements analysed.

Duplication
-----------

+-------------------------+------+---------+-----------+
|                         |now   |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines      |0     |NC       |NC         |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |NC       |NC         |
+-------------------------+------+---------+-----------+



Statistics by type
------------------

+---------+-------+-----------+-----------+------------+---------+
|type     |number |old number |difference |%documented |%badname |
+=========+=======+===========+===========+============+=========+
|module   |35     |NC         |NC         |0.00        |37.14    |
+---------+-------+-----------+-----------+------------+---------+
|class    |3      |NC         |NC         |0.00        |0.00     |
+---------+-------+-----------+-----------+------------+---------+
|method   |24     |NC         |NC         |8.33        |87.50    |
+---------+-------+-----------+-----------+------------+---------+
|function |39     |NC         |NC         |15.38       |41.03    |
+---------+-------+-----------+-----------+------------+---------+



Raw metrics
-----------

+----------+-------+------+---------+-----------+
|type      |number |%     |previous |difference |
+==========+=======+======+=========+===========+
|code      |785    |72.69 |NC       |NC         |
+----------+-------+------+---------+-----------+
|docstring |99     |9.17  |NC       |NC         |
+----------+-------+------+---------+-----------+
|comment   |72     |6.67  |NC       |NC         |
+----------+-------+------+---------+-----------+
|empty     |124    |11.48 |NC       |NC         |
+----------+-------+------+---------+-----------+



Messages by category
--------------------

+-----------+-------+---------+-----------+
|type       |number |previous |difference |
+===========+=======+=========+===========+
|convention |395    |NC       |NC         |
+-----------+-------+---------+-----------+
|refactor   |9      |NC       |NC         |
+-----------+-------+---------+-----------+
|warning    |112    |NC       |NC         |
+-----------+-------+---------+-----------+
|error      |16     |NC       |NC         |
+-----------+-------+---------+-----------+



% errors / warnings by module
-----------------------------

+----------------------------------+------+--------+---------+-----------+
|module                            |error |warning |refactor |convention |
+==================================+======+========+=========+===========+
|IpExpander.ipExpander             |50.00 |6.25    |0.00     |10.89      |
+----------------------------------+------+--------+---------+-----------+
|DNSRickroll.dnsRickroll           |18.75 |1.79    |0.00     |1.52       |
+----------------------------------+------+--------+---------+-----------+
|ECBPlaintextAttack.ecbServer      |12.50 |46.43   |0.00     |4.81       |
+----------------------------------+------+--------+---------+-----------+
|BurpVERBalyzer.VERBalyzer         |6.25  |7.14    |88.89    |24.81      |
+----------------------------------+------+--------+---------+-----------+
|FileIntegrity.fileintegrity       |6.25  |1.79    |0.00     |8.86       |
+----------------------------------+------+--------+---------+-----------+
|README                            |6.25  |0.00    |0.00     |0.00       |
+----------------------------------+------+--------+---------+-----------+
|BinToHex.binToHex                 |0.00  |9.82    |0.00     |1.27       |
+----------------------------------+------+--------+---------+-----------+
|PyDHCPDiscover.dhcpdiscover       |0.00  |8.93    |11.11    |16.46      |
+----------------------------------+------+--------+---------+-----------+
|AlexaPortScanner.alexaPortScanner |0.00  |4.46    |0.00     |2.03       |
+----------------------------------+------+--------+---------+-----------+
|ReverseShell.reverseShell         |0.00  |4.46    |0.00     |1.27       |
+----------------------------------+------+--------+---------+-----------+
|ZipCracker.zipCracker             |0.00  |3.57    |0.00     |2.53       |
+----------------------------------+------+--------+---------+-----------+
|ImageExtract.imgExtract           |0.00  |2.68    |0.00     |2.03       |
+----------------------------------+------+--------+---------+-----------+
|RSAGenKey.genKey                  |0.00  |0.89    |0.00     |7.34       |
+----------------------------------+------+--------+---------+-----------+
|ECBPlaintextAttack.ecbAttack      |0.00  |0.89    |0.00     |7.09       |
+----------------------------------+------+--------+---------+-----------+
|PortScanner.portScanner           |0.00  |0.89    |0.00     |1.52       |
+----------------------------------+------+--------+---------+-----------+



Messages
--------

+-----------+------------+
|message id |occurrences |
+===========+============+
|C0103      |277         |
+-----------+------------+
|C0111      |93          |
+-----------+------------+
|W0312      |56          |
+-----------+------------+
|C0301      |21          |
+-----------+------------+
|F0401      |12          |
+-----------+------------+
|E0602      |9           |
+-----------+------------+
|W0702      |7           |
+-----------+------------+
|W0622      |7           |
+-----------+------------+
|W0611      |7           |
+-----------+------------+
|R0201      |7           |
+-----------+------------+
|W0621      |6           |
+-----------+------------+
|W0612      |6           |
+-----------+------------+
|W0311      |4           |
+-----------+------------+
|E0611      |4           |
+-----------+------------+
|C0324      |4           |
+-----------+------------+
|W0703      |3           |
+-----------+------------+
|W0613      |3           |
+-----------+------------+
|W1401      |2           |
+-----------+------------+
|W0511      |2           |
+-----------+------------+
|W0402      |2           |
+-----------+------------+
|W0201      |2           |
+-----------+------------+
|W0105      |2           |
+-----------+------------+
|E1101      |2           |
+-----------+------------+
|W0232      |1           |
+-----------+------------+
|W0122      |1           |
+-----------+------------+
|W0107      |1           |
+-----------+------------+
|R0913      |1           |
+-----------+------------+
|R0912      |1           |
+-----------+------------+
|E0001      |1           |
+-----------+------------+



Global evaluation
-----------------
Your code has been rated at 1.82/10

External dependencies
---------------------
::

    Crypto 
      \-Cipher 
        \-AES (ECBPlaintextAttack.ecbServer)
    bs4 (ImageExtract.imgExtract)
    dns 
      \-resolver (DNSRickroll.dnsRickroll)
    requests (AndroidCloseTabs.androidCloseTabs,ImageExtract.imgExtract,FileIntegrity.fileintegrity)

Python Security Static Analysis

I know that I initially set out to perform static analysis on the security of these applications, but I was not successful.

That said, I found a few tools that I would like to try in the future. Stay tuned for a potential blog post but let me know if you have any recommendations!

  • Pyntch - Old and likely out-of-date
  • Python Taint - looks good but no longer maintained
  • Bandit - a new and relevant 'security linter from PyCQA'

I think a great first test for these would be my Python code injection demo, so I look forward to trying them out.

PyLint and Django-Lint - Conclusion

While static analysis didn't find me any vulnerabilities, it helped to get a little more familiar with these tools.

I think that Bandit will be the first security-based scanner that I try, but let me know if you have any other suggestions.

I hope to update my SecurityTools repository for all the errors at the very least, but feel free to submit pull requests for me!

doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.

As an Amazon Associate I earn from qualifying purchases.

Common passed on this blog, I made it to a jam.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.