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 testpasses (2390 unit tests, including the auto-discoveredmatcher-examplessuite — every one of the matcher's sub-patterns is exercised by an inline example) -
pnpm lintpasses -
pnpm knippasses -
pnpm -r buildpasses -
pnpm test:bundlepasses (28 tests) — a registered matcher compiles intodist/config.mjsviacreateDefaultRegistry, 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 = Falsein DB/index scripts, a NoderejectUnauthorized: false, acurl -kin a shell script). A further 14 candidates were inside vendoredvenv/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 initialverify=Falsepattern over-fired on unrelated kwargs (jwt.decode(..., verify=False),numpydispatch,mpmath.findroot), so it's now anchored to arequests/httpx/session/HTTP-verb call context on the same line. That's the one deliberate precision/recall trade: averify=Falsesplit across lines from its request call won't match. - Test-file exclusion is cross-language.
insecure-cryptoexcludes only JS-style.test./.spec.paths; this matcher also excludes_test.go,test_*.py/*_test.py,conftest.py,_spec.rb, andspec/dirs, since the scan-levelIGNORE_DIRSonly pre-excludes the JS conventions and this matcher is deliberately multi-language. filePatternsnames dot-path targets explicitly (.github/workflows/,**/.env,**/Dockerfile) because the scan glob doesn't traverse dot-paths by default — mirroringgithub-workflow-security,env-exposure, and thedockerfile-*matchers.- Overlap with
crypto-usage: Gotls.Config{...}lines are also picked up bycrypto-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-appif you'd like the matcher exercised by the fixture-based tests too — left it out to keep the diff to matcher + unit tests.
@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.