Skip to content

Fix CodeQL note-severity alerts: uncaught NumberFormatException in the tools, the GUI and SNMP - #829

Merged
vharseko merged 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:codeql/nfe-tools
Aug 4, 2026
Merged

Fix CodeQL note-severity alerts: uncaught NumberFormatException in the tools, the GUI and SNMP#829
vharseko merged 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:codeql/nfe-tools

Conversation

@vharseko

@vharseko vharseko commented Aug 3, 2026

Copy link
Copy Markdown
Member

Fifth batch of note-severity code scanning fixes (after #814, #817, #823 and #826): the java/uncaught-number-format-exception alerts left in the command line tools, the GUI and the SNMP connection handler — 43 of the 75 remaining alerts of this rule.

SNMP (10 alerts)

The ten counter getters of DsApplIfOpsEntryImpl repeated the same block parsing a connection handler statistic:

if (stats != null) {
  long value = Long.parseLong((String) this.monitor.getAttribute(stats, "searchRequests"));
  return SNMPMonitor.counter32Value(value);
} else {
  return 0L;
}

A statistic which is missing or does not hold a number failed the SNMP request with a runtime exception. The ten blocks now share a helper which returns zero in that case, which is what the getters already did when the statistics MBean itself was unavailable.

Per the review, getDsApplIfProtocol() in the same file had the array-typed variant of the bug: ds-connectionhandler-listener is multi-valued (one value per listen address), so a connection handler with several listen-address values comes through JMX as an Object[] and the (String) cast failed the SNMP request with a ClassCastException. The getter now takes the first listener — every listener of a connection handler shares the same port, and only the port is used — and still returns the default protocol OID when the attribute is missing.

Control panel and setup UI (18 alerts)

  • Utilities.getMonitoringValue() displays a monitoring value which is not a number as it is, exactly like it already does for an unparsable GMT date.
  • BuildInformation treats a missing or malformed version component as zero instead of breaking compareTo/equals with a runtime exception.
  • UIFactory.getColor() logs an invalid colour definition and falls back to black rather than failing the setup UI at class initialization.
  • The panels (IndexPanel, NewIndexPanel, NewBaseDNPanel, JavaArgumentsDialog, LocalOrRemotePanel, TaskToSchedulePanel, Installer) parse their fields with a new Utils.parseIntOrDefault(value, defaultValue) and a meaningful default — the index's current entry limit, DEFAULT_ENTRY_LIMIT, no entry to generate, and so on. All of these fields are validated before being used, so the default only applies if validation was bypassed.

Command line tools (15 alerts)

  • IntegerArgument gained getDefaultIntValue(fallback), so that tools read the typed default of an argument instead of parsing getDefaultValue() by hand: ConnectionFactoryProvider (5), InstallDS (4) and the replication CLI parser (1).
  • InstallDS reads --sampleData through getIntValue() and reports the parser's own error message instead of throwing.
  • WaitForFileDelete parses its timeout once, inside the handler which already exists for it, and reuses the value in the timeout message.
  • TaskTool computes the port defensively when deciding whether a connection failure was a wrong-port one.
  • The LDAP toolkit falls back to the default percentiles; the argument parser only accepts integers in [0, 100], so the fallback is unreachable in practice.
  • Utils.checkJavaVersion() treats an unparsable or absent java.specification.version as incompatible — with the usual message and exit code — instead of failing with a runtime exception.

Alerts deliberately left open (32)

All of them are in opendj-ldap-sdk-examples and opendj-embedded-server-examples, where each sample parses its command line arguments directly (final int port = Integer.parseInt(args[1]);). These files are included into the documentation through JCite markers, and wrapping every sample in argument-validation boilerplate would defeat their purpose. If the noise is unwanted, the cleaner option is to add those two modules to paths-ignore in the CodeQL workflow, since they are not part of the shipped product.

Testing

  • Full test suites of the modules touched upstream of the server: opendj-core 8173 tests, opendj-config 1038/547, opendj-cli 46, opendj-ldap-toolkit 291, opendj-server 3 — all passing.
  • opendj-server-legacy (-Pprecommit), covering the areas touched here: SNMPSyncManagerV2AccessTest (12), SNMPTrapManagerTest (1), InstallationTest (37), ConfigurationTest (9), SimplifiedViewEntryPanelTestCase (8), DuplicateEntryPanelTestCase (6), ScheduleTypeTest (5), TableViewEntryPanelTestCase (4), QuickSetupTestCase — 82 tests, all passing.

…e tools, the GUI and SNMP

Parse the numbers coming from monitoring data, configuration resources and
command line or GUI input with a proper fallback or error, instead of letting
NumberFormatException escape (java/uncaught-number-format-exception):

* The ten SNMP counter getters of DsApplIfOpsEntryImpl repeated the same block
  parsing a connection handler statistic; they now share a helper which returns
  zero when the statistic is missing or does not hold a number, rather than
  failing the SNMP request.
* The control panel displays a monitoring value which is not a number as it is,
  like it already does for unparsable dates; a build information file without a
  version number no longer breaks version comparison; an invalid colour
  definition in the resources is reported and replaced with black rather than
  breaking the setup UI.
* The panels parse their fields with Utils.parseIntOrDefault() and a meaningful
  default (the current index entry limit, the default entry limit, no entry to
  generate...). The fields are validated before being used, so the default only
  applies when validation was bypassed.
* IntegerArgument gained getDefaultIntValue(), so that the command line tools
  read the typed default of an argument instead of parsing getDefaultValue():
  ConnectionFactoryProvider, InstallDS and the replication CLI parser use it.
  InstallDS also reads --sampleData with getIntValue() and reports the parser
  error, WaitForFileDelete parses its timeout once inside the existing handler
  and reuses it, TaskTool computes the port defensively when diagnosing a
  connection failure, and the ldap toolkit falls back to the default percentiles.
* checkJavaVersion() treats an unparsable or absent java.specification.version
  as incompatible, with the usual message, instead of failing with a runtime
  exception.

The 32 remaining alerts of this rule are in opendj-ldap-sdk-examples and
opendj-embedded-server-examples, where the sample programs parse their command
line arguments directly. These files are included into the documentation through
JCite markers, and wrapping every one of them in error handling would defeat
their purpose, so they are left as they are.
@vharseko
vharseko requested a review from maximthomas August 3, 2026 13:00
@vharseko vharseko added security Security fixes / CodeQL code-scanning alerts java Pull requests that update java code bug labels Aug 3, 2026
Comment on lines 90 to 91

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ds-connectionhandler-listener is multi-valued (no SINGLE-VALUE in the schema; ConnectionHandlerMonitor:111 passes a LinkedList<HostPort>, filled per listen address at LDAPConnectionHandler:628). With two or more listen addresses JMXMBean.getJmxAttribute() returns an Object[], and this (String) cast throws the same ClassCastException as the ten counter getters fixed in this PR. CodeQL missed it because it's a cast, not a number parse — which is exactly why it's worth fixing while the file is open. Take the first listener and go through String.valueOf, keeping the existing null branch intact.

Suggested change
Object listener = this.monitor.getAttribute(
this.connectionHandlerName, "ds-connectionhandler-listener");
if (listener instanceof Object[]) {
// A connection handler with several listen addresses reports them as an array.
Object[] listeners = (Object[]) listener;
listener = listeners.length > 0 ? listeners[0] : null;
}
String portNumber = listener != null ? String.valueOf(listener) : null;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — I verified the whole chain: the schema defines ds-connectionhandler-listener without SINGLE-VALUE, ConnectionHandlerMonitor.getMonitorData() publishes one value per listen address, and JMXMBean.getJmxAttribute() wraps several values into an Object[] which SNMPMonitor.getAttribute() hands back as-is. The catch inside SNMPMonitor's PrivilegedAction does not cover the cast, which runs in the caller, so with two or more listen-address values the SNMP request failed with the ClassCastException — the same failure mode as the ten counter getters. This is also the only place in the SNMP module reading that attribute.

Applied as suggested in 8270c12: taking the first listener is enough because every listener of a connection handler shares the same listenPort, and this method only extracts the port after the last colon. SNMPSyncManagerV2AccessTest (12) and SNMPTrapManagerTest (1) re-run and pass.

… on a connection handler with several listen addresses
@vharseko
vharseko requested a review from maximthomas August 3, 2026 20:43
@vharseko
vharseko merged commit 4c7057e into OpenIdentityPlatform:master Aug 4, 2026
17 checks passed
@vharseko
vharseko deleted the codeql/nfe-tools branch August 4, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug java Pull requests that update java code security Security fixes / CodeQL code-scanning alerts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants