Skip to content

VNX-486: Comparison of Classes by Name

An object's type is decided by comparing its class name: in Java, getClass().getName(), getSimpleName() or getCanonicalName() (or Foo.class.getName()) compared with equals/equalsIgnoreCase/== in either order; in C#, GetType().Name or GetType().FullName compared with ==, != or Equals; in Python, type(x).__name__ or x.__class__.__name__ compared with ==, != or in. A switch (C# switch expression, Python match) on such a name is reported, as is a class name stored in a local and compared with a literal within the next eight lines. Different classes can share a name (another package, another class loader, an attacker-supplied subclass or plugin), so a trust or dispatch decision made this way can be satisfied by the wrong type. Not reported: instanceof, isinstance, is, isAssignableFrom and class-object identity (getClass() == Foo.class, GetType() == typeof(Foo), type(x) is Foo), a class name read for logging or display, and the pattern in comments or string literals.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-486
Severity Medium
SARIF level warning
Kind sast
Languages Java, C#, Python
CWE CWE-486
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 class-comparison type-confusion cwe-486
Source internal/sast/rules/vnx-486.rego

Overview

VNX-486 maps to CWE-486: Comparison of Classes by Name. It reports code that decides what type an object is by comparing the class’s name string instead of the class itself.

Why This Matters

A name is not an identity. Two classes loaded by different class loaders, a class in another package or module with the same simple name, or a plugin an attacker controls can all carry the name the check expects. When the comparison guards trust (“is this the trusted plugin?”, “is this an admin principal?”), the wrong type passes.

What Gets Flagged

if (plugin.getClass().getName().equals("com.acme.plugins.TrustedPlugin")) { ... }  // FLAGGED
if ("AdminHandler".equals(h.getClass().getSimpleName())) { ... }                 // FLAGGED
if (caller.GetType().Name == "TrustedCaller") return true;   // FLAGGED
if type(principal).__name__ == "AdminUser":   # FLAGGED
    return True

Not flagged: plugin instanceof TrustedPlugin, plugin.getClass() == CorePlugin.class, caller is TrustedCaller, isinstance(principal, AdminUser), and reading a class name for a log line.

Remediation

  1. Compare types, not strings: instanceof / Class.isAssignableFrom / getClass() == Foo.class in Java, is / typeof(Foo) in C#, isinstance or type(x) is Foo in Python.
  2. Where a name must come from configuration, resolve it once through a trusted class loader and compare the resulting class object.

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

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

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