VNX-703: Optional value read with get() without checking that it is present
Reports Java Optional values unwrapped with get()/getAsInt()/getAsLong()/getAsDouble() without a presence check: findById(...).get() and other Spring Data finders such as findByUsername(...).get(), findFirst().get(), stream max/min(...).get(), OptionalInt max().getAsInt(), and an Optional (or var assigned from a finder) whose get() is not preceded by isPresent()/isEmpty().
| Field | Value |
|---|---|
| Rule ID | VNX-703 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Java |
| CWE | CWE-703 |
| 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 | improper-check-exceptional optional cwe-703 |
| Source | internal/sast/rules/vnx-703.rego |
Overview
VNX-703 maps to CWE-703: Improper Check or Handling of Exceptional Conditions. An empty Optional is an expected outcome: no row has this id, no element matches the filter. get() turns that outcome into an unchecked NoSuchElementException, so a request for an unknown id becomes an HTTP 500 with a stack trace instead of a 404.
The rule reports, in production .java files:
- Spring Data
findById(...).get(); findFirst().get()andfindAny().get(), including.get()on the next line of a split chain;- stream
max(...)/min(...)andreduce(X::y)followed by.get(); IntStream/LongStream/DoubleStreammax()/min()/average()/findFirst()followed bygetAsInt/getAsLong/getAsDouble;Optional.ofNullable(...).get();- a variable declared
Optional<T>(orOptionalInt/OptionalLong/OptionalDouble) whoseget()is called within the next 15 lines with noisPresent(),isEmpty(),ifPresent()or null test of it in between or on the same line.
Not reported: orElse, orElseGet, orElseThrow, ifPresent, a presence test before the get(), Map.get, Future.get, List.get, Collections.max(...).get(...), test files, comments and string literals. Optionals passed between methods or stored in fields are not followed.
What Gets Flagged
return orders.findById(id).get(); // FLAGGED
return list.stream().max(Comparator.comparing(Order::total)).get(); // FLAGGED
Optional<Account> account = repo.findByLogin(login);
return account.get().getEmail(); // FLAGGED
What Is Not Flagged
return orders.findById(id).orElseThrow(() -> new NotFoundException(id));
Optional<Account> a = repo.findByLogin(login);
if (a.isEmpty()) {
return "";
}
return a.get().getName();
Remediation
- Use
orElseThrow(() -> new NotFoundException(...))so the absent case maps to a deliberate error (and a 404). - Use
orElse/orElseGetwhen a default is meaningful, orifPresentfor side effects.
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-703
// nosec vnx-703
Or record an ignore rule: vulnetix ignore add --rule vnx-703 --reason "...". See Suppressing findings.