Block Hardcoded Secrets in Commits using Pre-Commit

title_image

Pre-commit hooks allow you to run arbitrary code on your commit before it actually gets committed. 🤯

pre-commit is perfect for blocking yourself from committing hardcoded secrets.

I think even the biggest proponents of checking code for hardcoded secrets will still, every now and then, accidentally commit a hardcoded secret and push it up to GitHub. For most of us, it’s really not a matter of if, but when.

Even if it’s a private repo — who is going to see that code? If it’s a public repo, then you’d better rotate that key ASAP before the horde of GitHub scraper bots find your key.

Let’s dive into pre-commit hooks. 🪝

Example

A developer wrote the following code to call a stock API and forgot to replace the hardcoded secret with an environment variable before committing and pushing up to GitHub.

import requests

token = "3X8J0d5J5bp3JKXFgroJ"

url = "https://examplestockapi.com/api/v1/test"
params = {"symbol": "AAPL", "token": token}
stock_info = requests.get(url, params=params).json()

...

Notice the three dots at the bottom of the code block. These are important for later.

As you can see from this PyCharm screenshot, the developer successfully committed the code, which we don’t want.

img1

To demonstrate how pre-commit hooks would block this, first, we have to install the package. I’m going to install it using brew install pre-commit but there are several other ways.

Next, we have to create a YAML file that sits in the root directory of your repository. This file is called .pre-commit-config.yaml.

img2

In the .pre-commit-config.yaml, we can add specific hooks from GitHub repositories or even write and store them locally.

In our specific YAML file, we are going to add ripsecrets, which blocks you from committing secrets, as well as black, which is a Python code formatter. I want to demonstrate both hooks, because pre-commit has an every day usefulness that goes far beyond just a “blocker of committing secrets”.

Here is what our .pre-commit-config.yaml should look like:

repos:
-   repo: https://github.com/psf/black
    rev: 22.10.0
    hooks:
    -   id: black
-   repo: https://github.com/sirwart/ripsecrets.git
    rev: v0.1.5
    hooks:
    -   id: ripsecrets

We can also tell pre-commit to update the revisions for us by running pre-commit autoupdate.

Once the pre-commit config is added to the repo, we just have to run pre-commit install in the root directory of the repo.

img3

Since we are adding pre-commit hooks to an already-established repo, we will run pre-commit run --all-files to get the historical files up to the standard that our hooks will maintain going forward.

img4

The Failed output is what we are expecting to see: black thought my code was shitty because of the three dots, and ripsecrets found our hardcoded secret to call the stock API and blocked it.

If I remove the three dots in my code and try to commit again, you can see that black passes my code, but ripsecrets still doesn’t like the secret being there.

img5

img6

Now, let me try re-writing my code to use an environment variable instead of the hardcoded secret:

import requests
import os

token = os.environ["examplestockapi_token"]

url = "https://examplestockapi.com/api/v1/test"
params = {"symbol": "AAPL", "token": token}
stock_info = requests.get(url, params=params).json()

Now, when I try to commit, everything passes!

img7