From e96fda71f218cc79b07d44089408be807e25e850 Mon Sep 17 00:00:00 2001 From: farkhalit rida Date: Wed, 22 Jul 2026 12:24:33 +0530 Subject: [PATCH 1/3] reject an empty option name in getMatchingOptions An empty name after hyphen stripping matched every long option, so the token "--=value" bound the value to a long option that was never named. --- src/main/java/org/apache/commons/cli/Options.java | 4 ++++ .../java/org/apache/commons/cli/OptionsTest.java | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/org/apache/commons/cli/Options.java b/src/main/java/org/apache/commons/cli/Options.java index 40d8b1103..6f23f72ed 100644 --- a/src/main/java/org/apache/commons/cli/Options.java +++ b/src/main/java/org/apache/commons/cli/Options.java @@ -219,6 +219,10 @@ public Options addRequiredOption(final String opt, final String longOpt, final b public List getMatchingOptions(final String opt) { final String clean = Util.stripLeadingHyphens(opt); final List matchingOpts = new ArrayList<>(); + // an empty name is not a partial name, it would match every long option + if (Util.isEmpty(clean)) { + return matchingOpts; + } // for a perfect match return the single option only if (longOpts.containsKey(clean)) { return Collections.singletonList(clean); diff --git a/src/test/java/org/apache/commons/cli/OptionsTest.java b/src/test/java/org/apache/commons/cli/OptionsTest.java index f0ac95258..4bbc93100 100644 --- a/src/test/java/org/apache/commons/cli/OptionsTest.java +++ b/src/test/java/org/apache/commons/cli/OptionsTest.java @@ -182,6 +182,19 @@ void testGetMatchingOpts() { assertToStrings(options.getOption("verbose")); } + @Test + void testGetMatchingOptsEmptyName() throws Exception { + final Options options = new Options(); + options.addOption(Option.builder("c").longOpt("config-file").hasArg().get()); + assertTrue(options.getMatchingOptions("").isEmpty()); + assertTrue(options.getMatchingOptions("-").isEmpty()); + assertTrue(options.getMatchingOptions("--").isEmpty()); + // "--=value" names no option, so it must not bind a value to config-file + for (final CommandLineParser parser : new CommandLineParser[] { new DefaultParser(), new PosixParser() }) { + assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "--=/etc/shadow" })); + } + } + @Test void testGetOptionsGroups() { final Options options = new Options(); From 556c981d7c4ca5a23c5dfdca39d4f611740a340b Mon Sep 17 00:00:00 2001 From: farkhalit rida Date: Wed, 29 Jul 2026 11:11:36 +0530 Subject: [PATCH 2/3] Document and test null-tolerant getMatchingOptions Signed-off-by: farkhalit rida --- src/main/java/org/apache/commons/cli/Options.java | 6 +++--- src/test/java/org/apache/commons/cli/OptionsTest.java | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/cli/Options.java b/src/main/java/org/apache/commons/cli/Options.java index 6f23f72ed..9a924ba48 100644 --- a/src/main/java/org/apache/commons/cli/Options.java +++ b/src/main/java/org/apache/commons/cli/Options.java @@ -212,14 +212,14 @@ public Options addRequiredOption(final String opt, final String longOpt, final b /** * Gets the options with a long name starting with the name specified. * - * @param opt The partial name of the option. - * @return The options matching the partial name specified, or an empty list if none matches. + * @param opt The partial name of the option, or {@code null}. + * @return The options matching the partial name specified, or an empty list if none matches or the name is null or empty. * @since 1.3 */ public List getMatchingOptions(final String opt) { final String clean = Util.stripLeadingHyphens(opt); final List matchingOpts = new ArrayList<>(); - // an empty name is not a partial name, it would match every long option + // a null or empty name is not a partial name; empty would match every long option if (Util.isEmpty(clean)) { return matchingOpts; } diff --git a/src/test/java/org/apache/commons/cli/OptionsTest.java b/src/test/java/org/apache/commons/cli/OptionsTest.java index 4bbc93100..d6b2f4667 100644 --- a/src/test/java/org/apache/commons/cli/OptionsTest.java +++ b/src/test/java/org/apache/commons/cli/OptionsTest.java @@ -186,6 +186,7 @@ void testGetMatchingOpts() { void testGetMatchingOptsEmptyName() throws Exception { final Options options = new Options(); options.addOption(Option.builder("c").longOpt("config-file").hasArg().get()); + assertTrue(options.getMatchingOptions(null).isEmpty()); assertTrue(options.getMatchingOptions("").isEmpty()); assertTrue(options.getMatchingOptions("-").isEmpty()); assertTrue(options.getMatchingOptions("--").isEmpty()); From e0531ce864cd444a185014f7de0d274fa34e3504 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 1 Aug 2026 14:04:29 -0400 Subject: [PATCH 3/3] Update Javadoc for getMatchingOptions method --- src/main/java/org/apache/commons/cli/Options.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/cli/Options.java b/src/main/java/org/apache/commons/cli/Options.java index 9a924ba48..dcb6b52b4 100644 --- a/src/main/java/org/apache/commons/cli/Options.java +++ b/src/main/java/org/apache/commons/cli/Options.java @@ -212,7 +212,7 @@ public Options addRequiredOption(final String opt, final String longOpt, final b /** * Gets the options with a long name starting with the name specified. * - * @param opt The partial name of the option, or {@code null}. + * @param opt The partial name of the option, may be {@code null}. * @return The options matching the partial name specified, or an empty list if none matches or the name is null or empty. * @since 1.3 */