Limited Spots: Interested in sponsoring?
Author Trevor I. Lasn

Trevor I. Lasn

/dev/writer

5 min read

SecretLint — A Linter for Preventing Committing Credentials

A guide to catching and preventing credential leaks in your code using Secretlint

According to GitHub’s secret scanning alerts, thousands of secrets are leaked every month. The scary part? Many of these leaks go unnoticed for days or even weeks.

It happens more often than we’d like to admit. I’ve seen AWS keys accidentally pushed to GitHub, API tokens sitting in plain text in config files, and private keys casually hanging out in documentation.

The cost of a leaked credential can be enormous - both in terms of security risks and engineering time spent rotating keys. Taking the time to set up proper safeguards now can save countless hours of incident response later.

That’s where Secretlint comes in. It’s an open-source tool I’ve been using extensively to catch these issues before they become problems.

Getting Started with Secretlint

You have two main options for installing Secretlint:

docker run -v `pwd`:`pwd` -w `pwd` --rm -it secretlint/secretlint secretlint "**/*"
  1. Using NPM (Better for local development):

I prefer using NPM for local development as it integrates better with other development tools. Let’s walk through that setup.

npm install secretlint @secretlint/secretlint-rule-preset-recommend --save-dev

Next, we’ll initialize Secretlint in our project.

  trevorlasn.com git:(master)  npx secretlint --init

This creates a .secretlintrc.json file in the root of our project.

{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-preset-recommend"
    }
  ]
}

Running Secretlint for trevorlasn.com to see if it catches any secrets.

Create /Users/trevorindreklasn/Projects/trevorlasn.com/.secretlintrc.json
  trevorlasn.com git:(master)  npx secretlint "**/*"

  trevorlasn.com git:(master)

Looks good, I don’t have any secrets in my codebase. To demonstrate how Secretlint works, I’ve created a controlled example below. This shows the type of sensitive information Secretlint is designed to catch:

Running Secretlint on this example triggers our security checks:

  trevorlasn.com git:(master)  npx secretlint 'src'

/Users/trevorindreklasn/Projects/trevorlasn.com/src/content/blog/secret-lint/InsecureKeyDisplay.tsx
  12:25  error  [PrivateKey] found private key: -----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCYdGaf5uYMsilGHfnx/zxXtihdGFr3hCWwebHGhgEAVn0xlsTd
1QwoKi+rpI1O6hzyVOuoQtboODsONGRlHbNl6yJ936Yhmr8PiNwpA5qIxZAdmFv2
tqEllWr0dGPPm3B/2NbjuMpSiJNAcBQa46X++doG5yNMY8NCgTsjBZIBKwIDAQAB
AoGAN+Pkg5aIm/rsurHeoeMqYhV7srVtE/S0RIA4tkkGMPOELhvRzGmAbXEZzNkk
nNujBQww4JywYK3MqKZ4b8F1tMG3infs1w8V7INAYY/c8HzfrT3f+MVxijoKV2Fl
JlUXCclztoZhxAxhCR+WC1Upe1wIrWNwad+JA0Vws/mwrEECQQDxiT/Q0lK+gYaa
+riFeZmOaqwhlFlYNSK2hCnLz0vbnvnZE5ITQoV+yiy2+BhpMktNFsYNCfb0pdKN
D87x+jr7AkEAoZWITvqErh1RbMCXd26QXZEfZyrvVZMpYf8BmWFaBXIbrVGme0/Q
d7amI6B8Vrowyt+qgcUk7rYYaA39jYB7kQJAdaX2sY5gw25v1Dlfe5Q5WYdYBJsv
0alAGUrS2PVF69nJtRS1SDBUuedcVFsP+N2IlCoNmfhKk+vZXOBgWrkZ1QJAGJlE
FAntUvhhofW72VG6ppPmPPV7VALARQvmOWxpoPSbJAqPFqyy5tamejv/UdCshuX/
9huGINUV6BlhJT6PEQJAF/aqQTwZqJdwwJqYEQArSmyOW7UDAlQMmKMofjBbeBvd
H4PSJT5bvaEhxRj7QCwonoX4ZpV0beTnzloS55Z65g==
-----END RSA PRIVATE KEY-----  @secretlint/secretlint-rule-preset-recommend > @secretlint/secretlint-rule-privatekey

 1 problem (1 errors, 0 warnings)

  trevorlasn.com git:(master) 

The error above shows how Secretlint identifies private keys in your codebase. In a real project, this would prevent the accidental commit of sensitive credentials. This example uses a non-functional key for demonstration purposes only.

What Secretlint Can Detect

Beyond private keys, Secretlint can identify various types of sensitive information:

  • AWS access keys and secret keys
  • Google Cloud credentials
  • API tokens and keys
  • Database connection strings
  • Basic authentication credentials
  • And more through custom rules

Setting Up Pre-commit Hooks

Pre-commit hooks are like security guards for your git repository. They run checks on your code before each commit, making sure no sensitive data slips through. Here’s how to set them up using Husky and lint-staged:

# Install dependencies
npm install --save-dev husky lint-staged

# Initialize Husky
npx husky-init

This creates a .husky directory in your project with the basic hook setup. Next, we’ll configure it to run Secretlint.

Update your package.json with these settings:

{
  "scripts": {
    "prepare": "husky install"
  },
  "lint-staged": {
    "*": "secretlint"
  }
}
  • The prepare script runs automatically after npm install, setting up Husky for anyone who clones your repository
  • lint-staged configuration tells Husky to run Secretlint on all files that are staged for commit

Finally, add the pre-commit hook:

npx husky add .husky/pre-commit "npx lint-staged"

Now, when you try to commit a file containing secrets, here’s what happens:

 git commit -m "Update config"
🔍 Running Secretlint...
 secretlint found 1 problem(s)
  ./config.js
    1:10  error  Found AWS Access Key: AKIA...

commit aborted

Want to temporarily skip these checks? (Use with caution!)

git commit -m "message" --no-verify

But seriously, if you find yourself wanting to skip these checks, it’s usually a sign that something needs fixing in your code, not in your commit process.

Remember to add these files to your .gitignore

node_modules
.husky/_

Advanced Configuration

Secretlint’s real power comes from its customization options. Let’s dive into some advanced configurations I use in different scenarios:

Customizing Rules

The basic configuration is just the start. Here’s how I structure more sophisticated rule sets:

{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-preset-recommend",
      "rules": [
        {
          "id": "@secretlint/secretlint-rule-aws",
          "options": {
            "allows": [
              "/^AKIA_EXAMPLE_/",
              "/^DUMMY_AWS_/"
            ]
          }
        },
        {
          "id": "@secretlint/secretlint-rule-github",
          "options": {
            "allows": [
              "/^ghp_EXAMPLE/"
            ]
          }
        }
      ]
    },
    {
      "id": "@secretlint/secretlint-rule-pattern",
      "options": {
        "patterns": [
          {
            "name": "custom-api-key",
            "pattern": "/MY_API_KEY_[A-Z0-9]{32}/",
            "message": "Found custom API key"
          }
        ]
      }
    }
  ]
}

Handling False Positives

False positives can be frustrating. Here are three ways I handle them:

  1. Using allowMessageIds
{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-aws",
      "allowMessageIds": [
        "AWSAccountID"  // Ignore AWS account ID detections
      ]
    }
  ]
}
  1. Using Rule-Specific Allows
{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-privatekey",
      "options": {
        "allows": [
          "/BEGIN PUBLIC KEY/",  // Allow public keys
          "/EXAMPLE_PRIVATE_KEY/" // Allow example keys in docs
        ]
      }
    }
  ]
}
  1. Using Inline Comments
// secretlint-disable-next-line
const exampleKey = "AKIA_EXAMPLE_KEY";

/* secretlint-disable */
const testData = {
  key: "test_key",
  secret: "test_secret"
};
/* secretlint-enable */

While Secretlint is great at catching credentials before they make it into your codebase, it’s just one piece of the security puzzle. I’ve found it works best when combined with proper secret management systems, environment variables for configuration, regular security audits, and ongoing team education about security practices.

References


Learning Paths & Resources

Level up your skills with these curated learning resources from trusted educational partners. Perfect for developers at any stage who want to master frontend, backend, DevOps, or tackle real-world coding challenges.


This article was originally published on https://www.trevorlasn.com/blog/secret-lint. It was written by a human and polished using grammar tools for clarity.

Interested in a partnership? Shoot me an email at hi [at] trevorlasn.com with all relevant information.