Skip to content

Add tls-verification-disabled matcher - #148

Open
Steel-tech wants to merge 3 commits into
vercel-labs:mainfrom
Steel-tech:tls-verification-disabled-matcher
Open

Add tls-verification-disabled matcher#148
Steel-tech wants to merge 3 commits into
vercel-labs:mainfrom
Steel-tech:tls-verification-disabled-matcher

Conversation

@Steel-tech

Copy link
Copy Markdown

What changed

Adds a new built-in matcher, tls-verification-disabled, that flags code which explicitly disables TLS certificate or hostname verification (CWE-295) across the ecosystems deepsec already scans.

Why

There was no built-in coverage for disabled TLS verification — no matcher referenced rejectUnauthorized, InsecureSkipVerify, verify=False, VERIFY_NONE, sslmode=disable, or curl -k. It's a high-signal, cross-language weakness class, so a single ungated matcher fills the gap in one place. Every pattern anchors to the explicit disable form (false / 0 / disable / skip-verify), so verification-enabled configurations never match.

Covered idioms: Node (rejectUnauthorized: false, NODE_TLS_REJECT_UNAUTHORIZED=0, strictSSL: false), Go (InsecureSkipVerify: true), Python (requests/httpx verify=False, ssl._create_unverified_context, check_hostname = False), Ruby (VERIFY_NONE), JVM (permissive hostname verifiers / trust-all strategy), PHP (CURLOPT_SSL_VERIFY* off), .NET (validation-callback bypass), curl/wget/git CLI flags, and connection strings (sslmode=disable, tls=skip-verify, insecure-skip-tls-verify).

Verification

  • pnpm test passes (2390 unit tests, including the auto-discovered matcher-examples suite — every one of the matcher's sub-patterns is exercised by an inline example)
  • pnpm lint passes
  • pnpm knip passes
  • pnpm -r build passes
  • pnpm test:bundle passes (28 tests) — a registered matcher compiles into dist/config.mjs via createDefaultRegistry, so it's part of the publish surface
  • Ran it against real repositories and confirmed the candidate count is sane

Real-repo candidate counts (pnpm deepsec scan --matchers tls-verification-disabled):

  • App codebase A: 5 candidates, all true positives (production Postgres SSL config with rejectUnauthorized: false).
  • App codebase B: 5 app-code candidates, all true positives (check_hostname = False in DB/index scripts, a Node rejectUnauthorized: false, a curl -k in a shell script). A further 14 candidates were inside vendored venv/site-packages/ (urllib3/httpx internals) — genuine matches, filtered by ordinary project ignore config rather than by the matcher.

Notes for reviewer

  • Noise tier precise — the matched syntax is itself a strong signal. During validation, the initial verify=False pattern over-fired on unrelated kwargs (jwt.decode(..., verify=False), numpy dispatch, mpmath.findroot), so it's now anchored to a requests/httpx/session/HTTP-verb call context on the same line. That's the one deliberate precision/recall trade: a verify=False split across lines from its request call won't match.
  • Test-file exclusion is cross-language. insecure-crypto excludes only JS-style .test./.spec. paths; this matcher also excludes _test.go, test_*.py/*_test.py, conftest.py, _spec.rb, and spec/ dirs, since the scan-level IGNORE_DIRS only pre-excludes the JS conventions and this matcher is deliberately multi-language.
  • filePatterns names dot-path targets explicitly (.github/workflows/, **/.env, **/Dockerfile) because the scan glob doesn't traverse dot-paths by default — mirroring github-workflow-security, env-exposure, and the dockerfile-* matchers.
  • Overlap with crypto-usage: Go tls.Config{...} lines are also picked up by crypto-usage's wide-net "Go TLS op" pattern. Different tiers and purposes (wide-net-for-AI vs. precise disable signal), so this is additive, not a conflict — flagging in case you'd prefer a note or de-dup.
  • Happy to add a positive fixture under fixtures/vulnerable-app if you'd like the matcher exercised by the fixture-based tests too — left it out to keep the diff to matcher + unit tests.

Detects disabled TLS certificate/hostname verification (CWE-295) across
Node (rejectUnauthorized, NODE_TLS_REJECT_UNAUTHORIZED, strictSSL), Go
(InsecureSkipVerify), Python (verify=False, unverified context,
check_hostname), Ruby (VERIFY_NONE), JVM (permissive verifiers), PHP
(CURLOPT_SSL_VERIFY*), .NET (validation callback bypass), curl/wget/git
CLI flags, and connection strings (sslmode=disable, tls=skip-verify,
insecure-skip-tls-verify).

Every regex anchors to the explicit disable form so enabled
configurations never match. Skips test files across ecosystems, since
the scan-level ignore list only covers JS-style test paths.
Real-repo validation showed the bare `verify=False` pattern firing on
unrelated kwargs (jwt.decode, numpy dispatch, mpmath.findroot). Require a
requests/httpx/session token or an HTTP-verb call on the same line so the
pattern only matches TLS verification bypass.
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@Steel-tech is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant