#148

Add tls-verification-disabled matcher

OpenSteel-techwants to merge intofrom

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.

Activity

4
vercel[bot]3mo ago

@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.