Skip to content

VNX-439: Behavioral Change in New Version or Environment

Reports Python security APIs whose behaviour depends on the interpreter version: hmac.new() or hmac.HMAC() without a digest, ssl.SSLContext() with no or the generic PROTOCOL_TLS/PROTOCOL_SSLv23 protocol, module-level ssl.wrap_socket(), ssl.match_hostname() and cgi.escape().

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-439
Severity Low
SARIF level note
Kind sast
Languages Python
CWE CWE-439
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags behavioral-change-new-version python-version cwe-439
Source internal/sast/rules/vnx-439.rego

Overview

VNX-439 maps to CWE-439: Behavioral Change in New Version or Environment. The same line of code is weak on one Python version and broken, or silently different, on another, so its security depends on the runtime it happens to be deployed on.

What Gets Flagged

  • hmac.new() or hmac.HMAC() with no digestmod= and fewer than three positional arguments: MD5 before Python 3.8, TypeError from 3.8.
  • ssl.SSLContext() with no protocol, or with ssl.PROTOCOL_TLS/PROTOCOL_SSLv23: role and verification defaults vary by version (deprecated since 3.10).
  • Module-level ssl.wrap_socket(): no certificate or hostname verification by default, removed in 3.12.
  • ssl.match_hostname(): removed in 3.12.
  • cgi.escape(): removed in 3.8, and leaves quotes unescaped by default.

From-imported names (from ssl import wrap_socket, from cgi import escape) are recognised.

mac = hmac.new(SECRET, payload)                         # FLAGGED
ctx = ssl.SSLContext()                                  # FLAGGED
tls = ssl.wrap_socket(raw)                              # FLAGGED

mac = hmac.new(SECRET, payload, hashlib.sha256)         # not flagged
ctx = ssl.create_default_context()                      # not flagged

Not reported: hmac.digest(), SSLContext(ssl.PROTOCOL_TLS_CLIENT)/PROTOCOL_TLS_SERVER, context.wrap_socket(), html.escape(), comments and strings. Explicitly obsolete TLS versions are VNX-PY-021; tar extraction without a filter is VNX-409.

Remediation

  1. Always pass digestmod to hmac.new().
  2. Use ssl.create_default_context() or SSLContext(ssl.PROTOCOL_TLS_CLIENT) and wrap sockets through the context with server_hostname=.
  3. Replace cgi.escape() with html.escape().

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-439
// nosec vnx-439

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

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