Skip to content

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.

Updated 02 Nov 2026Checked against cli@de9b52a
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, LocalDateTime or Instant;
  • 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

  1. Use equals() or Objects.equals(a, b) (null-safe) for boxed numbers, UUIDs and dates.
  2. Use compareTo(..) == 0 for BigDecimal, where equals also compares scale.
  3. 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.

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