Skip to content

VNX-1058: Invokable Control Element in Multi-Thread Context with non-Final Static Storable or Member Element

Reports a non-final, non-volatile static field of a non-thread-safe type in a multi-threaded Java class (servlet, filter, Spring bean, JAX-RS resource, ContentProvider, Runnable/Thread or executor user) that a method writes without holding a lock, or a static formatter or calendar it uses unguarded. Constants, atomic and concurrent types, static-initialiser-only writes and tests are not reported.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-1058
Severity Low
SARIF level note
Kind sast
Languages Java
CWE CWE-1058
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 multi-thread-non-final-static concurrency race-condition cwe-1058
Source internal/sast/rules/vnx-1058.rego

Overview

VNX-1058 maps to CWE-1058. It reports a static field that is neither final nor volatile, of a type that is not thread-safe, in a Java class that runs on several threads at once, when a method writes the field without holding a lock: an assignment, compound assignment or increment, or a mutating call (put, add, remove, clear, append, …) outside the declaration, any static initialiser and main(), while the enclosing method is not synchronized and no synchronized block or Lock.lock() opens between that method’s header and the write (a synchronized sibling method does not protect it). A static SimpleDateFormat, DateFormat, NumberFormat, MessageFormat or Calendar field also counts when it is only used (format, parse, setTime), because those calls mutate it. The finding is on the field’s declaration.

A class counts as multi-threaded when it is a request-handling or managed singleton (extends HttpServlet/GenericServlet, implements Filter, is annotated @Controller, @RestController, @Service, @Component, @Repository, @Endpoint or @Path, or extends an Android ContentProvider) or starts concurrent work itself (implements Runnable/Callable, extends Thread, uses an ExecutorService, new Thread, CompletableFuture, parallelStream, @Async or @Scheduled).

Why This Matters

A static field is shared by every request and every thread. Unsynchronised writes lose updates, corrupt HashMap internals and leak one user’s data into another user’s response (a lastUser field set by one request and read by the next).

Not reported: static final constants, volatile fields, Atomic*, LongAdder, Concurrent*, CopyOnWrite*, ThreadLocal and Collections.synchronized* fields, static fields that are only read or only set in a static initialiser, classes with no concurrency signal, test files and comments.

What Gets Flagged

public class HitServlet extends HttpServlet {
    private static int hits = 0;
    private static Map<String, Integer> perUser = new HashMap<>();

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        hits++;
        perUser.put(req.getRemoteUser(), 1);
    }
}

Not flagged:

private static AtomicInteger hits = new AtomicInteger();

public static synchronized void increment() {
    count++;
}

Remediation

  1. Make the field final and immutable, or move the state into a request-scoped or instance field.
  2. Use an atomic or concurrent type (AtomicInteger, LongAdder, ConcurrentHashMap).
  3. Otherwise guard every read and write with the same lock.

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

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

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