VNX-595: Boxed number or value object compared by reference (== / !=)
Reports Java == and != between two variables declared as boxed numbers or value objects (Integer, Long, BigDecimal, UUID, Date...), and comparisons of an object with a freshly constructed wrapper or a boxed valueOf(), which compare references instead of values.
| Field | Value |
|---|---|
| Rule ID | VNX-595 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Java |
| CWE | CWE-595 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | java object-identity boxing cwe-595 |
| Source | internal/sast/rules/vnx-595.rego |
Overview
VNX-595 maps to CWE-595: Comparison of Object References Instead of Object Contents. In Java, == and != on two objects compare identity. Two Integers holding 1000 are different objects, so a == b is false.
The rule reports:
- a comparison whose two operands are both variables declared (as a parameter, local or field within the 60 lines above) as
Integer,Long,Short,Byte,Character,Double,Float,BigDecimal,BigInteger,UUID,Date,LocalDate,LocalDateTimeorInstant; - an object compared with
new Integer(..),new Long(..),new Boolean(..)and the other wrappers, which is never identical to anything; - an object compared with a boxed
Integer.valueOf(..)and friends.
It does not report primitives, comparisons with null, enum constants or this, equals/compareTo/Objects.equals, a boxed value compared with a primitive or a literal (Java unboxes and compares values), operands whose type is unknown (method calls), or comments and string literals. String comparison with == is VNX-597.
Why This Matters
The Integer cache makes boxed values from -128 to 127 identical, so the bug passes every test that uses small ids and fails in production once the values grow. In an access check (ownerId == currentUserId) that turns into an authorisation decision that depends on the size of a number.
What Gets Flagged
boolean sameAccount(Integer fromId, Integer toId) {
return fromId == toId; // FLAGGED
}
if (value == new Integer(0)) { } // FLAGGED: never true
return status == Integer.valueOf(404); // FLAGGED (status is an Integer)
// NOT FLAGGED
return fromId.equals(toId);
return x.compareTo(y) == 0;
if (code == null || cached == expected) { } // expected is an int
Remediation
- Use
equals()orObjects.equals(a, b)(null-safe) for boxed numbers, UUIDs and dates. - Use
compareTo(..) == 0forBigDecimal, whereequalsalso compares scale. - Unbox explicitly (
a.intValue() == b.intValue()) when both values are known to be non-null.
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-595
// nosec vnx-595
Or record an ignore rule: vulnetix ignore add --rule vnx-595 --reason "...". See Suppressing findings.