Ethical Hacking + Python III - Tests

Vamos a mejorar la calidad de nuestro código e implementar pruebas unitarias en Habu https://gitlab.com/securetia/habu

HACKINGPYTHON

Introducción

Tarde o temprano, el código que escribimos va a dejar de funcionar. Por modificar algo en una librería, por no contemplar una determinada dependencia o por mil otras razones diferentes. Sabiendo que va a fallar, tenemos que intentar manejar esos fallos de la mejor manera posible.

Eso lo logramos a través de dos cosas:

  • Pruebas unitarias

  • Integración contínua

Además, tenemos que pensar que nuestro código tiene que ser mantenido, ya sea por nosotros o por alguna otra persona, por lo tanto, tenemos que volver ese código fácil de leer y entender. Eso lo logramos siguiendo las mejores prácticas para la escritura de código.

En este artículo vamos a resolver esos tres puntos.

Pruebas Unitarias

Las pruebas unitarias apuntan a verificar que las porciones de nuestro código (por ejemplo, funciones de una librería) funcionan como deberían. Dichas pruebas también van a ser escritas en código Python, y van a ser simples funciones que verifican los resultados devueltos por las funciones que queremos probar. Python, en su librería estándar, ya cuenta con el módulo unittest, pero nosotros vamos a utilizar el módulo PyTest, que es más fácil de utilizar y ofrece salidas más completas y claras.

A continuación, vamos a crear el directorio ‘tests’ dentro del directorio principal de nuestro proyecto. Y vamos a escribir algunos tests sencillos para nuestra librería xor. Estos tests los vamos a poner en un archivo llamado ‘test_xor.py’. El hecho de que el nombre del archivo empiece con ‘test’ y esté dentro del directorio ‘tests’, le permite a PyTest encontrar las pruebas y ejecutarlas de una forma sencilla.

El contenido del archivo ‘test_xor.py’ va a ser el siguiente:

Como podemos ver, tenemos dos funciones, las dos muy parecidas.

La primera, ‘test_xor()’, xorea el texto de la variable ‘text’ y luego verifica que al volver a xorearlo, se obtenga el texto original.

La función ‘asset’ nos permite verificar una condición y hacer que el éxito o el fracaso del test dependa de ella.

En este caso, estamos verificando que ‘text’ sea igual al resultado de ‘xor(encrypted)‘.

La segunda, ‘test_xor_w_key()’, hace lo mismo que la primera, pero utiliza el parámetro que define la clave con la cual vamos a xorear.

Si guardamos el archivo y ejecutamos el comando ‘pytest -v’ (el parámetro ‘-v’, como es habitual, habilita el modo verbose, para que obtengamos más datos acerca de la ejecución de pytest):

Vamos a generar que la prueba falle (cambiamos el == por un !=), es decir, la prueba espera que text sea diferente al resultado de xor(encrypted).

Como podemos ver, los tests pasaron (‘PASSED’). Si modificamos la función ‘test_xor()’ y la cambiamos por:

Claramente, PyTest nos está explicando qué fue lo que falló.

Deberíamos escribir pruebas para todas las funciones, o por lo menos, para las que consideremos más importantes.

Esto también nos fuerza a escribir funciones pequeñas, que hagan cosas claras.

Es muy difícil crear tests para funciones complejas.

Nota: En los ejemplos, podemos ver que los tests que acabo de escribir no son perfectos. Un simple caso de error sería el caso en el que la función xor() no esté haciendo absolutamente nada, y devolviera los valores sin modificar. En ese caso, las pruebas serían exitosas. Eso podríamos arreglarlo mejorando las pruebas, o creando otras pruebas adicionales, que verifiquen puntualmente el hecho de que el resultado de ‘xor()’ sea el esperado.

Integración Contínua

Con la integración contínua hacemos que nuestros tests sean ejecutados en uno o varios ambientes, para verificar que no funcionan únicamente en nuestras PC.

(Es muy habitual que las cosas funcionen solo en la máquina del desarrollador, y esto es un claro signo de que algo anda mal)

Como Habu es un proyecto hosteado en GitLab, podemos utilizar directamente su solución de integración contínua de forma gratuita.

(Para los que hosteen sus repositorios en GitHub, pueden utilizar Travis
https://travis-ci.org/).

Para que GitLab sepa que queremos utilizar su CI, debemos generar un archivo llamado ‘.gitlab-ci.yml’ en el directorio raíz de nuestro repositorio.

En el caso de Habu, el archivo contiene lo siguiente:

Como podemos ver, son simplemente los comandos que debemos ejecutar para obtener un entorno en el cual se pueda instalar Habu y ejecutar sus tests.

Cada vez que hagamos un nuevo commit del código, veremos el resultado de los tests en la interfaz web de GitLab.

NOTA: Si tienen dudas sobre el funcionamiento de GitLab-CI, pueden visitar el siguiente link: https://docs.gitlab.com/ce/ci/quick_start/README.html.

Otro de los cambios que debemos hacer para que esto funcione, es agregar las dependencias de PyTest y PyTest-Runner (este último ayuda a que sea más fácil ejecutar de forma automática PyTest).

Para esto, editamos el archivo ‘setup.py’, y lo configuramos de la siguiente forma:

Las partes importates para lo que vimos hasta ahora son:

  1. tests_require: Define qué paquetes son necesarios para ejecutar los tests

  2. test_suite: Define cuál es el comando a ejecutar para correr los tests

Calidad de Código

Python cuenta con varias mejores prácticas a la hora de escribir código. La mayoría de las cuales están detalladas en el documento PEP8 https://www.python.org/dev/peps/pep-0008/.

Para verificar que nuestro código cumple con dichas mejores prácticas, y validar algunas otras que no están definidas en PEP8, podemos utilizar el módulo ‘flake8’, que reúne varias pruebas y las pone a disposición a través del comando ‘flake8’.

Ahora, si ejecutamos ‘flake8’, podremos ver algo como lo siguiente:

Veremos varios problemas que agregué a propósito para demostrar el funcionamiento de flake8, y no están en el repositorio de código.

Más allá de cuestiones de estilo, como el hecho de dejar o no espacios entre operadores, podemos ver que nos está alertando sobre la importación de un módulo que no ha sido utilizado (datetime).

Corregir todas estas alertas puede ser tedioso, pero hace que nuestro código sea mucho más legible y aceptable por toda la comunidad Python.

En el caso de que flake8 no envie ninguna salida por pantalla, quiere decir que no ha encontrado ningún error.

Conclusión

Hemos visto varias formas de mejorar nuestro código, y detectar las fallas que pudieran introducir nuestras modificaciones lo antes posible.

En los próximos artículos

Vamos a agregar nuevas funcionalidades a Habu, más relacionadas con el Ethical Hacking (perdón, pero hablar de la calidad del código era necesario antes de seguir escribiendo nuevas funcionalidades).

an abstract photo of a curved building with a blue sky in the background
Escaneá vulnerabilidades de manera continua con Vulseek

Registrate ahora y empezá a usarlo gratis.

Paraguay 643 3º A, CABA, Argentina | Email: info@securetia.com | © Securetia

Política de calidad