Skip to content

Copy the Option before applying property values in handleProperties - #433

Open
farkhalit wants to merge 1 commit into
apache:masterfrom
farkhalit:property-option-copy
Open

Copy the Option before applying property values in handleProperties#433
farkhalit wants to merge 1 commit into
apache:masterfrom
farkhalit:property-option-copy

Conversation

@farkhalit

Copy link
Copy Markdown

DefaultParser.handleProperties calls processValue on the Option instance owned by the caller's Options rather than on a copy, so a value that arrives through the defaults Properties is written into the shared Option and stays there after the parse. Reuse that same Options for a second parse and the isValuesEmpty guard silently serves the first parse's value instead of the second one's, the value remains readable through options.getOption(...), and two threads parsing at once write the same values list. Parser.processProperties has the same write-through and also puts the shared Option on the CommandLine, so a later parse rewrites what an earlier CommandLine returns.

Both property paths now clone before storing the value, which is what the argv paths already do in handleOption and processArgs. The new test in AbstractParserTestCase fails for DefaultParser, BasicParser, GnuParser and PosixParser without the change.

  • Read the contribution guidelines for this project.
  • Read the ASF Generative Tooling Guidance if you use Artificial Intelligence (AI).
  • I used AI to create any part of, or all of, this pull request. Which AI tool was used to create this pull request, and to what extent did it contribute?
  • Run a successful build using the default Maven goal with mvn; that's mvn on the command line by itself.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible, but it is a best practice.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body. Note that a maintainer may squash commits during the merge process.

@garydgregory garydgregory changed the title copy the Option before applying property values in handleProperties Copy the Option before applying property values in handleProperties Jul 21, 2026
@garydgregory
garydgregory requested a review from Copilot July 24, 2026 12:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Fixes property-based parsing so that option values from Properties are applied to a cloned Option rather than mutating the caller-owned Options, preventing value “write-through” across parses and threads.

Changes:

  • Clone Option instances in both property-processing paths before applying processValue() / adding to CommandLine.
  • Add a regression test ensuring reusing the same Options across parses doesn’t leak values into earlier CommandLines or into Options.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/test/java/org/apache/commons/cli/AbstractParserTestCase.java Adds regression test covering Options reuse across property-based parses.
src/main/java/org/apache/commons/cli/Parser.java Clones options in processProperties before setting values / adding to CommandLine.
src/main/java/org/apache/commons/cli/DefaultParser.java Clones options in handleProperties before setting values / passing to handleOption.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +287 to +290
// the Options belong to the caller and outlive this parse, so hold the value on a copy
final Option copy = (Option) opt.clone();
if (copy.hasArg()) {
if (copy.isValuesEmpty()) {
Comment on lines +521 to +525
// the Options belong to the caller and outlive this parse, so hold the value on a copy
final Option copy = (Option) opt.clone();
if (copy.hasArg()) {
if (copy.isValuesEmpty()) {
copy.processValue(stripLeadingAndTrailingQuotesDefaultOff(value));
@garydgregory

Copy link
Copy Markdown
Member

@farkhalit
Please review the Copilot comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants