VNX-820: Missing Synchronization
A Java object that one instance serves every request thread (a class extending HttpServlet/GenericServlet/HttpFilter, a servlet filter extending OncePerRequestFilter/GenericFilterBean or implementing javax/jakarta Filter, a Servlet or HandlerInterceptor implementation, or a Spring @RestController/@Controller/@Service/@Component/@Repository singleton) keeps mutable state in a field and a request-handling method writes it with no synchronization: an assignment, compound assignment or ++/-- of a non-final, non-volatile field, or a mutating call (put, add, remove, clear, merge, compute*, append...) on a field holding a HashMap, ArrayList, HashSet, LinkedList, TreeMap, LinkedHashMap, ArrayDeque or StringBuilder, declared with that type or declared final as a Map/List/Set and given one in a constructor. Concurrent requests then race on it (lost updates, corrupted collections, one user's data shown to another). Not reported: writes inside a synchronized method or synchronized block, writes while an explicit Lock taken in the method (lock()/writeLock().lock()) is still held, Atomic*, Concurrent*, CopyOnWrite*, ThreadLocal, Collections.synchronized* and volatile fields, injected fields (@Autowired/@Inject/@Value/@Resource), writes in constructors, static initialisers, init()/afterPropertiesSet()/@PostConstruct methods and setters, locals and parameters that shadow the field, classes that are not shared (entities, DTOs), beans scoped prototype/request/session, test files and comments.
| Field | Value |
|---|---|
| Rule ID | VNX-820 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Java |
| CWE | CWE-820 |
| 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 | missing-sync cwe-820 concurrency race-condition |
| Source | internal/sast/rules/vnx-820.rego |
Overview
VNX-820 maps to CWE-820: Missing Synchronization. Servlets and Spring singletons are shared by every request thread; a field written from a request handler without a lock is a data race.
What Gets Flagged
public class HitServlet extends HttpServlet {
private int hits;
private String lastUser;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
hits++; // FLAGGED
lastUser = req.getParameter("user"); // FLAGGED: one user's value seen by another
}
}
@Component
public class SessionCache {
private final Map<String, String> sessions = new HashMap<>();
public void remember(String id, String user) {
sessions.put(id, user); // FLAGGED: HashMap mutated concurrently
}
}
Not flagged: ConcurrentHashMap/AtomicInteger fields, writes inside synchronized, constructor and init() writes, @Scope("prototype") beans, locals that shadow the field.
// FLAGGED: a classic servlet Filter shares one instance
public class RequestCounterFilter implements Filter {
private long served;
public void doFilter(ServletRequest rq, ServletResponse rs, FilterChain c) {
served++;
}
}
Not flagged: writes made between lock.lock() and lock.unlock() on a ReentrantLock field.
Remediation
- Keep per-request data in locals or the request/session, not in fields.
- Use concurrent types (
ConcurrentHashMap,AtomicLong,LongAdder) for shared counters and caches. - Guard compound updates with
synchronizedor aLock.
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-820
// nosec vnx-820
Or record an ignore rule: vulnetix ignore add --rule vnx-820 --reason "...". See Suppressing findings.