Mike Vanbuskirk

The Dig

May 18, 2023

Running TruffleHog in a GitHub action

Running TruffleHog in a GitHub action

Mike Vanbuskirk

May 18, 2023

TruffleHog provides the ability to scan existing resources and locations for critical secrets, but did you know that there’s a GitHub Action for scanning new commits and pull requests as well?

Scanning for secrets in a CI/CD pipeline like GitHub Actions means you can catch potential secret disclosure before they move farther along the deployment process. In this article, we’ll show you how to use the TruffleHog GitHub action to automatically update pull request diffs with scanner results.

It’s worth noting secrets exposed in source code need to be rotated, or revoked asap, and unlike other vulnerabilities, preventing a production deploy doesn’t fix the vulnerability, but rather can just save time and headache in dealing with rotation when production services aren’t yet dependent on the secret.

PREREQUISITES

This workflow depends on having an application in a GitHub repository that utilizes secrets or credentials, as well as access to and some familiarity with GitHub Actions. We’ll this example repository for demonstration purposes.

The GitHub Actions used in the example are:

https://github.com/marketplace/actions/trufflehog-oss

EXAMPLE APPLICATION

Our example application is a basic Python program that uses the Boto3 module to query an AWS account for a list of S3 buckets, which it will then output. I’ve got a basic GitHub Actions workflow configured that checks out the code, scans it with the TruffleHog scanner, and reports back any detected secrets. I’ve also got some basic Python pre-commit checks installed.

SETTING UP THE TRUFFLEHOG GITHUB ACTION

The first thing we need to do is configure our TruffleHog GitHub action to automatically scan whenever certain events occur; in this example we’re interested in pull requests, and the hopefully infrequent push to main. 

The full configuration can be found here, but I’ll highlight the important sections in the article.
This section ensures that this workflow always runs for any pull requests and pushes to main branch:


on: 
  push: 
    branches: 
      - main 
  pull_request


This block ensures that the GitHub Action workflow has permissions to write comments and update pull requests:


permissions: 
  contents: read 
  id-token: write 
  issues: write 
  pull-requests: write


This block provides the top level job configuration, including name and image, as well as the first step. Setting `fetch-depth` to `0` ensures that no tags or branches are excluded from the scan.


jobs: 
  TruffleHog: 
    runs-on: ubuntu-latest 
    defaults: 
      run: 
        shell: bash 
    steps: 
      - name: Checkout code 
        uses: actions/checkout@v3 
        with: 
          fetch-depth: 0

.

We’ll set up the TruffleHog action as follows:


  - name: TruffleHog OSS 
id: trufflehog 
uses: trufflesecurity/trufflehog@add-actions-cli-switch 
continue-on-error: true 
with: 
  path: ./ 
  base: "${{ github.event.repository.default_branch }}" 
  head: HEAD 
  extra_args: --debug --only-verified


Finally, we’ll make sure that if a secret is detected, the run fails:


- name: Scan Results Status 
  if: steps.trufflehog.outcome == 'failure' 
  run: exit 1


With this configuration, any time someone pushes a code change directly to main, or via a pull request, the TruffleHog scanner will scan the code for any secrets, validate that they’re live, and the run will fail if any live secrets are detected. What’s more, the scanner will now update the pull request diff view with specific callouts of any credentials that are present.

SECRET DISCLOSURE

With the GitHub Action workflow configured, we can test out the scanner by committing some sensitive values to source, and seeing if we get a valid warning. We’ll be using https://canarytokens.org/ to generate some AWS credentials that don’t do anything, but will report as valid when checked against the API.

In this example, a developer has created a branch called “update-app”, which certainly seems innocuous enough. Once a pull request has been created with a commit, the GitHub Actions workflow configured in the previous section will be triggered, and the scanner will run. Lo and behold, it looks like our developer wasn’t too careful with their AWS credentials:



On the main page of the pull request, reviewers can also see that the check has failed, indicating that either a secret was detected, or there was an issue with the workflow.


 

In either case, a merge would be avoided until the issue is resolved.
So, we’d really like to avoid having AWS credentials in our source code, so the next logical step is to push another commit that removes them. For the sake of this example, we’ll just clear the configuration strings. If this were a live application, the best-practice would be to make use of environment variables and GitHub Actions secrets. Our “developer” pushes the commit, and all is well right? Unfortunately no; our scan will still fail, even though it appears as though the issue has been resolved:



So what’s going on? This behavior is an excellent example of the benefits of having automated testing and scanning in a CI/CD pipeline versus manual code review, particularly in the context of security. What’s happening here is that the secret is still present, it’s just “wedged” between commits. The TruffleHog scanner is checking the entire Git history; even though the latest commit removes the credentials, they are still present in the Git history. If this were merged and the credentials were not deactivated, it would still be possible for a bad actor to find and abuse them.



The only way to safely remedy the issue is to revoke or invalidate the credentials, rendering them permanently inactive, after which the scanner will no longer alert for a valid credential finding.

LIMITATIONS

Automatically detecting secrets in pull requests before they’re merged to the main branch is a huge win. However, there are limitations to this approach that can still leave secrets exposed.

  • Abandoned branches or pull requests: What happens when a branch or pull request is abandoned or closed? If there were valid secrets, they will still be present and discoverable until deactivated.

  • Secret rotation: As mentioned above, removing the secret from source code does not actually invalidate it. Unfortunately there isn’t currently a way to automatically deactivate discovered secrets via GitHub Actions; detected secrets will require an engineer with access to the relevant provider to deactivate them.

  • Limited scope: This process will only detect and alert on secrets that are found inside the context of an active pull request. It won’t necessarily detect active secrets in other branches that have already been merged, and it won’t detect active secrets in other repositories.

FINDING SECRETS

Implementing security automation in CI/CD is a great example of “shifting left”: a term from DevSecOps that means moving security work into earlier phases of the Software Development Lifecycle (SDLC). It’s far easier and cheaper to find and fix secret exposure early; the cost of publicly compromised credentials can be catastrophic for the affected company.
However, secrets can end up in a lot more places than the commits of a specific pull request. Active secrets may be lurking in other source code repositories, S3 buckets, Jira tickets, Confluence pages, and even ChatOps tools like Slack and MS Teams. The best way to improve security posture is with defense in depth: use workflows like CI/CD automation and pre-commit hooks to prevent secrets from making it into live environments, and rely on active scanning of all locations to quickly identify and remediate exposed secrets.

Lifecycle tracking of exposed secrets is also really important, to ensure secrets leaked are properly rotated. Once code lands on GitHub it’s subject to be cloned, copied, and further propagated out.