# Staying safe while using third-party packages in Python


>Whenever we are building something with Python, most of the time we rely on third-party packages so that we don't have to reinvent the wheel for some simple things like sending an HTTP request. By doing so we lose control of how safe we are with that decision. If you want to learn how to prevent security holes inside of your application or service, read below :)

## Introduction

Before I explain the simple way **HOW** to prevent security holes that you could have inside of your application, we should first understand **WHY** this is so important.
Let me kick off this article with some interesting stats:

- Cybercrime is up ~600% due to the COVID-19 pandemic
- In 2020, the average time to identify a breach was 207 days
- 43% of cyberattacks target small businesses
- $3.86 million is the global average cost of a data breach

Here comes the scariest one:

- **95% of cybersecurity breaches are a result of human error**

In other words - 95% of the above **could have been prevented**. Unfortunately, human neglect is what leads to these numbers. Most of these could be prevented simply by making use of any sort of vulnerability scanner.

In my article, I will explain in detail how to **utilize a simple scanner** that will keep your dependencies safe :)

## 1. Dependency problem

Let's assume you are building an app that will act as currency exchange software (GUI, API, ...). You will need to hook up to some sort of **external API** to get the exchange rate list that you will rely on to do the currency conventions. Now comes the **tricky part** - unless you will do **EVERYTHING** from scratch, you will probably make use of [`requests`](https://pypi.org/project/requests/) (3rd party) package.

This is where things are getting messy. For the `requests` to work, that 3rd party package A**LSO uses other 3rd party packages** and installs them inside of your app (docker image, venv, server ...). If you check the [`setup.py`](https://github.com/psf/requests/blob/main/setup.py) from the `requests` package you can see exactly which 3rd party packages are needed for requests to work:

```
requires = [
    'charset_normalizer~=2.0.0; python_version >= "3"',
    'chardet>=3.0.2,<5; python_version < "3"',
    'idna>=2.5,<3; python_version < "3"',
    'idna>=2.5,<4; python_version >= "3"',
    'urllib3>=1.21.1,<1.27',
    'certifi>=2017.4.17'
]
```

Not that bad, right? Well, the thing is that every one of these can **ALSO** have sub-packages they need to run themselves. 

### 1.1 Building the complete dependency tree

Ok, let's see all of this in action. Create a new project and create a [virtual environment](https://docs.python.org/3/library/venv.html) along with it. Activate the `venv` you just created:

`source /path-to-your-venv/bin/activate`

Once your venv is activated, create a file (outside of your venv in your project root) called `requirements.in` and put the following inside of it:

```
aiohttp
requests
``` 

For the fun of it, I also added the [`aiohttp`](https://pypi.org/project/aiohttp/) package.
Now let's install `pip-tools` to compile the dependency tree:

`pip install pip-tools`

Build the dependency tree by executing the following:

`pip-compile -r requirements.in`

After it is done, you will notice a new file in your project root called `requirements.txt`. That's what we were looking for - **the complete dependency tree**. All of the requirements are there:

```
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
#    pip-compile requirements.in
#
aiohttp==3.8.1
    # via -r requirements.in
aiosignal==1.2.0
    # via aiohttp
async-timeout==4.0.2
    # via aiohttp
attrs==21.4.0
    # via aiohttp
certifi==2021.10.8
    # via requests
charset-normalizer==2.0.11
    # via
    #   aiohttp
    #   requests
frozenlist==1.3.0
    # via
    #   aiohttp
    #   aiosignal
idna==3.3
    # via
    #   requests
    #   yarl
multidict==6.0.2
    # via
    #   aiohttp
    #   yarl
requests==2.27.1
    # via -r requirements.in
urllib3==1.26.8
    # via requests
yarl==1.7.2
    # via aiohttp
``` 

Do you see how many there are? And we only included **two packages** for our application. Imagine how many there are once you are finished creating your application :)

## 2. Safety

Now that we know WHY we should use a vulnerability scanner, it is time to see **HOW** to do it.

[`Safety`](https://pypi.org/project/safety/) to the rescue.

Safety (also a 3rd party package!) is a dependency security scanner that checks all of your application dependencies against the Python vulnerability database [Safety DB](https://github.com/pyupio/safety-db). If there are any vulnerabilities found, `safety` will output them as a plain text or JSON format.
`Safety` can be used in many ways inside of your project and we will **explore most of them**.

### 2.1 Safety as a package

Straight forward way of using `safety` is to just install it as you would normally do with any other 3rd party package:

`pip install safety`

After that we can start the scanner by using the following command:

`safety check -r requirements.txt`

If everything is ok, you will get the following output (trimmed down):

```
| REPORT
| checked 12 packages, using free DB (updated once a month)
| No known security vulnerabilities found.
``` 

### 2.2 Safety inside of Docker image

Most of the time we will **Dockerize** our application for the sake of deployment/ease of use/ development/some other reason. `Safety` can fortunately also be used as a **scanner of Docker images**. Build your image as you would normally do and add a `TAG` to it. Afterward, run the command:

`docker run -it --rm ${TAG} "/bin/bash -c \"pip install safety && safety check\"`

### 2.3 Safety as a Docker image

If you don't want to install and run `safety` as a package, you can also make use of the Docker image of `safety`. That way you will hook up the contents of your `requirements.txt` and check them against the `safety` running inside of a Docker image:

`cat requirements.txt | docker run -i --rm pyupio/safety safety check --stdin`

### 2.4 Safety inside of CI service

If you are making use of CI services such as **GitLab**, you can also include `safety` as a part of your CI pipeline. Add the following as a step inside of any stage that you prefer in your `.gitlab-ci.yml`:

```
safety:
  script:
    - pip install safety
    - safety check
```

Make sure that the image that you use for your pipelines has `pip` installed

### 2.5 Safety inside of tox

You can also add `safety` as a part of your **[`tox`](https://pypi.org/project/tox/)** configuration:

```
[tox]
envlist = py39

[testenv]
deps =
    safety
    pytest
commands =
    safety check
    pytest
``` 

This way `safety` will run right before your tests execute.

### 2.6 Safety as a pre-commit hook

If you are using pre-commit hooks, `safety` can also be added inside of your `.pre-commit-config.yaml`:

```
-   repo: https://github.com/Lucas-C/pre-commit-hooks-safety
    rev: v1.2.4
    hooks:
    -   id: python-safety-dependencies-check
        files: requirements.txt
``` 

## 3. Conclusion

While there are **so many ways** to keep your application safe regardless of the tech stack it was built in, this time I just wanted to show you why making use of a vulnerability scanner **could be essential** for your project. It is fairly simple to include one inside of your Python projects and there should be no excuse for not using one in the first place.

Stay safe and happy coding!

Like always, thanks for reading :)
