Skip to content

VNX-358: Hand-rolled TLS host name or certificate identity check uses a partial string match

Reports hand-written TLS identity checks (Java HostnameVerifier implementations and lambdas, certificate subject/issuer name tests, Python verify_hostname-style functions) that compare with endsWith/startsWith/contains/in instead of the standard matching rules.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-358
Severity Low
SARIF level note
Kind sast
Languages Java, Python
CWE CWE-358
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:L/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags improper-standard-check tls hostname-verification cwe-358
Source internal/sast/rules/vnx-358.rego

Overview

VNX-358 maps to CWE-358: Improperly Implemented Security Check for Standard. TLS server identity has a standard (RFC 6125) and every platform ships an implementation of it. This rule reports hand-written replacements that check the identity with a partial string match:

  • Java: a HostnameVerifier verify(String host, SSLSession s) method, or a (host, session) -> lambda passed to setHostnameVerifier / setDefaultHostnameVerifier / hostnameVerifier, that tests the host with endsWith, startsWith, contains, indexOf or matches and does not delegate to another verifier’s verify().
  • Java: a certificate subject or issuer name (getSubjectDN(), getSubjectX500Principal(), getIssuerDN(), getIssuerX500Principal() then getName()), inline or through a variable assigned within six lines, tested with contains, startsWith, endsWith, indexOf or matches.
  • Python: a function named like verify_hostname, match_host or check_server_hostname whose host-name parameter is tested with endswith, startswith or find, or is the right side of x in host, unless the function also calls ssl.match_hostname or another verify routine.

Not reported: exact comparisons (equals, ==, a host looked up in an allowlist), a suffix test combined with a delegated verify() call, string tests on ordinary paths and names, Python host checks in functions that never touch a certificate, socket or SSL object (HTTP Host header validation is not TLS identity), test files, comments and string literals. A verifier that simply returns true is VNX-JAVA-006; switching verification off is VNX-CRYPTO-005.

Why This Matters

evilacme.com ends with acme.com, api.attacker.net starts with api., and any public CA will issue a certificate whose subject contains the text CN=api.acme.com inside another attribute. A partial match accepts all of them, so an attacker who can intercept traffic presents a certificate for a host they control and the client trusts it.

What Gets Flagged

// FLAGGED
public boolean verify(String hostname, SSLSession session) {
    return hostname.endsWith("acme.com");
}

conn.setHostnameVerifier((h, s) -> h.endsWith(".acme.com"));

String subject = chain[0].getSubjectX500Principal().getName();
if (!subject.contains("CN=api.acme.com")) { throw new CertificateException("untrusted"); }
# FLAGGED
def match_host(cert, host):
    cn = dict(x[0] for x in cert["subject"])["commonName"]
    return cn in host
// NOT FLAGGED: restricts the host, then delegates to the standard verifier
return hostname.endsWith(".acme.com") && delegate.verify(hostname, session);

Remediation

  1. Use the platform verifier: the default HostnameVerifier (or OkHttp’s), and in Python an ssl.create_default_context() with check_hostname = True.
  2. To trust a specific server, pin its public key (OkHttp CertificatePinner, a pinned TrustManager) rather than matching names as strings.
  3. If names must be compared, compare the exact parsed name, never a substring.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-358
// nosec vnx-358

Or record an ignore rule: vulnetix ignore add --rule vnx-358 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-358.rego, cli/internal/sast/docs/vnx-358.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.