Skip to content
jay-tankPublic

About

A zero-config, cross-language static gate that flags code disabling TLS/SSL certificate verification (verify=Fa InsecureSkipVerify, rejectUnauthorized:false...) - a silent man-in-the-middle exposure (CWE-295) - and points you atlse, the real fix. Single Go binary.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

Repository files navigation

trustfell

Flag code that turns off TLS certificate verification — before it ships.

Disabling certificate verification is the most common copy-paste "fix" for a TLS error: verify=False, rejectUnauthorized: false, InsecureSkipVerify: true. It makes the error go away, the tests pass, and review sees a normal HTTPS call. But the client now accepts any certificate from anyone: HTTPS has quietly become unauthenticated transport, and a machine on the network path can present its own certificate, decrypt the traffic, and re-encrypt it onward — a silent man-in-the-middle. It's a real, well-documented bug class (CWE-295, Improper Certificate Validation), and nothing fails when you introduce it.

trustfell is a zero-config, cross-language static gate that scans your code for disabled certificate verification and fails the build.

$ trustfell .

● 1 disabled TLS certificate verification:
  api/client.py:14:34  TLS certificate verification disabled (verify=False).
      ↳ Remove verify=False; pass a CA bundle path (verify='/path/ca.pem') or fix the real certificate.
      [TF001 · CWE-295]

1 blocker · 0 warnings

Exit code 1 when it finds one, so it drops straight into pre-commit or CI.

How it works

The detection is deliberately simple and language-agnostic. trustfell scans each source line for a curated set of fragments that disable certificate or hostname verification, tuned so they match only the disabling form. Every pattern is boundary-aware: verify=False fires, but verify=True and a custom CA path like verify='/etc/ssl/certs/ca-bundle.crt' never do; InsecureSkipVerify: true fires, InsecureSkipVerify: false does not.

No code is executed, no network calls are made, and there's nothing to configure. It's a single static binary.

Install

go install github.com/jay-tank/trustfell@latest

Or build from source: go build -o trustfell .

Usage

trustfell                 # scan the current directory
trustfell ./src           # scan a path
trustfell --json          # machine-readable output
trustfell --strict        # treat warnings (TF002) as failures too

In CI

- run: go run github.com/jay-tank/trustfell@latest ./

Exit codes: 0 clean · 1 a disable (TF001), or any finding under --strict · 2 usage error.

Rules

Rule Severity What
TF001 blocker An explicit disable of TLS certificate / hostname verification (CWE-295)
TF002 warning A related trust-weakening pattern that isn't a hard disable (e.g. suppressing urllib3's InsecureRequestWarning)

What each language's disable looks like — and the fix

Language / tool Disable (flagged) Fix instead
Python (requests/httpx) verify=False pass a CA bundle path, or fix the cert
Python (ssl) ssl._create_unverified_context(), ssl.CERT_NONE, check_hostname = False keep create_default_context() defaults
Python (aiohttp) ssl=False, verify_ssl=False pass a real ssl.SSLContext
Node (JS/TS) rejectUnauthorized: false, NODE_TLS_REJECT_UNAUTHORIZED=0 set the ca option to your bundle
Go InsecureSkipVerify: true set RootCAs to a real CA pool
Java NoopHostnameVerifier, ALLOW_ALL_HOSTNAME_VERIFIER, a HostnameVerifier returning true keep the platform-default verifier
C#/.NET ServerCertificateValidationCallback => true, DangerousAcceptAnyServerCertificateValidator validate the chain / pin the cert
curl / libcurl -k / --insecure, CURLOPT_SSL_VERIFYPEER = 0 pass --cacert / set CURLOPT_CAINFO

The honest fix in every case is the same: trust the right CA (or pin the certificate), don't disable the check.

Before / after

# before — trustfell: TF001
requests.get("https://internal.example", verify=False)

# after — verification stays on, internal CA trusted explicitly
requests.get("https://internal.example", verify="/etc/ssl/certs/internal-ca.pem")

Suppressing false positives

For the rare legitimate case (a pinned-cert client, an internal test harness), add a trustfell:ignore comment on the line:

requests.get(url, verify=False)  # trustfell:ignore reviewed: pinned internally

Or skip whole paths with a .trustfellignore file in the scanned root (one path-substring per line, # for comments).

Scope & limitations

trustfell answers one question well — "is TLS certificate verification being disabled here?" — across languages, as a fast static gate. Be honest about what that means:

  • It is a line-based heuristic, not data-flow. A disable split across many lines, or hidden behind a custom wrapper function, may be missed. (A multi-line Go tls.Config{ InsecureSkipVerify: true } is still caught on the InsecureSkipVerify: true line.)
  • It matches specific fragments only, so it will not invent findings for a truthy verify= value or a re-enabling line.
  • It does not prove a connection is otherwise secure; it catches the single most common shape of the bug.

License

MIT © Jay Tank

About

A zero-config, cross-language static gate that flags code disabling TLS/SSL certificate verification (verify=Fa InsecureSkipVerify, rejectUnauthorized:false...) - a silent man-in-the-middle exposure (CWE-295) - and points you atlse, the real fix. Single Go binary.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages