VNX-1023: Secret compared by prefix, suffix or truncation
Reports passwords, tokens, keys, signatures, digests, OTPs and PINs checked with startswith/endswith/regionMatches or compared after truncation in Python and Java, so an empty or short guess matches.
| Field | Value |
|---|---|
| Rule ID | VNX-1023 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Java, Python |
| CWE | CWE-1023 |
| 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 | incomplete-comparison authentication cwe-1023 |
| Source | internal/sast/rules/vnx-1023.rego |
Overview
VNX-1023 maps to CWE-1023: Incomplete Comparison with Missing Factors. It reports a secret (a name containing password, pwd, secret, token, key, signature, sig, digest, hmac, otp, pin or passcode) checked against another value by comparing only part of it:
X.startswith(Y)/endswith(Python) andX.startsWith(Y)/endsWith/regionMatches(...)(Java), where one side is a secret and the other a stored, expected or user-supplied value;- equality on truncated values: Python
a[:n] == bandhmac.compare_digest(a[:n], ...), Javaa.substring(0, n).equals(b)anda.equals(b.substring(0, n)).
Not reported: prefix checks against a string literal or a name that says it is a prefix, scheme or type (token.startswith("Bearer "), sig.startsWith(SIG_PREFIX)), tokenizer code, full-length comparisons, comments and string literals. Comparisons that omit a non-secret factor (a tenant or user id) are not statically recognisable and are not attempted.
Why This Matters
stored_key.startswith(provided) is true when provided is an empty string, and a truncated signature check can be brute-forced a few characters at a time. The comparison has dropped the factor that made it a secret check: the whole value.
What Gets Flagged
return stored_key.startswith(provided)
return submitted[:6] == expected_otp[:6]
return expectedToken.startsWith(supplied);
return computed.substring(0, 12).equals(signature);
Remediation
- Compare the full values in constant time:
hmac.compare_digest(a, b)in Python,MessageDigest.isEqual(a, b)in Java. - Reject empty or short input before comparing.
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-1023
// nosec vnx-1023
Or record an ignore rule: vulnetix ignore add --rule vnx-1023 --reason "...". See Suppressing findings.