Fix CodeQL note-severity alerts: uncaught NumberFormatException in the tools, the GUI and SNMP - #829
Conversation
…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.
There was a problem hiding this comment.
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.
| 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; |
There was a problem hiding this comment.
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
Fifth batch of note-severity code scanning fixes (after #814, #817, #823 and #826): the
java/uncaught-number-format-exceptionalerts 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
DsApplIfOpsEntryImplrepeated the same block parsing a connection handler statistic: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-listeneris multi-valued (one value per listen address), so a connection handler with severallisten-addressvalues comes through JMX as anObject[]and the(String)cast failed the SNMP request with aClassCastException. 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.BuildInformationtreats a missing or malformed version component as zero instead of breakingcompareTo/equalswith a runtime exception.UIFactory.getColor()logs an invalid colour definition and falls back to black rather than failing the setup UI at class initialization.IndexPanel,NewIndexPanel,NewBaseDNPanel,JavaArgumentsDialog,LocalOrRemotePanel,TaskToSchedulePanel,Installer) parse their fields with a newUtils.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)
IntegerArgumentgainedgetDefaultIntValue(fallback), so that tools read the typed default of an argument instead of parsinggetDefaultValue()by hand:ConnectionFactoryProvider(5),InstallDS(4) and the replication CLI parser (1).InstallDSreads--sampleDatathroughgetIntValue()and reports the parser's own error message instead of throwing.WaitForFileDeleteparses its timeout once, inside the handler which already exists for it, and reuses the value in the timeout message.TaskToolcomputes the port defensively when deciding whether a connection failure was a wrong-port one.[0, 100], so the fallback is unreachable in practice.Utils.checkJavaVersion()treats an unparsable or absentjava.specification.versionas 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-examplesandopendj-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 topaths-ignorein the CodeQL workflow, since they are not part of the shipped product.Testing
opendj-core8173 tests,opendj-config1038/547,opendj-cli46,opendj-ldap-toolkit291,opendj-server3 — 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.