Skip to content

Commit 5084211

Browse files
pr9tmacfarlajflo
authored
Chore: Prevent repeated cli boolean flags for being accepted (#8898)
* Chore: Prevent repeated cli boolean flags for being accepted Signed-off-by: Preeti <35308865+pr9t@users.noreply.github.com> * allow unmatched options Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Preeti <35308865+pr9t@users.noreply.github.com> Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Justin Florentine <justin+github@florentine.us>
1 parent f784ebf commit 5084211

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Remove deprecated `Quantity.getValue` method (deprecated since 2019) [#8968](https://github.com/hyperledger/besu/pull/8968)
99
- Support for block creation on networks running a pre-Byzantium fork is removed, after being deprecated for a few months. If still running a pre-Byzantium network, it needs to be updated to continue to produce blocks [#9005](https://github.com/hyperledger/besu/pull/9005)
1010
- Remove support for Ethereum protocol version `eth/67`. [#9008](https://github.com/hyperledger/besu/pull/9008).
11+
- Abort startup if boolean command line options are specified more than once [#8898](https://github.com/hyperledger/besu/pull/8898)
1112

1213
### Upcoming Breaking Changes
1314

app/src/main/java/org/hyperledger/besu/cli/BesuCommand.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,11 @@
253253
import picocli.CommandLine.Command;
254254
import picocli.CommandLine.ExecutionException;
255255
import picocli.CommandLine.IExecutionStrategy;
256+
import picocli.CommandLine.Model.ITypeInfo;
257+
import picocli.CommandLine.Model.OptionSpec;
256258
import picocli.CommandLine.Option;
257259
import picocli.CommandLine.ParameterException;
260+
import picocli.CommandLine.ParseResult;
258261

259262
/** Represents the main Besu CLI command that runs the Besu Ethereum client full node. */
260263
@SuppressWarnings("FieldCanBeLocal") // because Picocli injected fields report false positives
@@ -832,6 +835,17 @@ private int executeCommandLine(
832835
final BesuParameterExceptionHandler parameterExceptionHandler,
833836
final BesuExecutionExceptionHandler executionExceptionHandler,
834837
final String... args) {
838+
839+
try {
840+
// Parse and run duplicate-check
841+
// As this happens before the plugins registration and plugins can add options, we must
842+
// allow unmatched options
843+
final ParseResult pr = commandLine.setUnmatchedArgumentsAllowed(true).parseArgs(args);
844+
rejectDuplicateScalarOptions(pr); // your generic validator
845+
} catch (ParameterException e) {
846+
// ← Send it to the standard handler: prints one line & exits status 1
847+
return parameterExceptionHandler.handleParseException(e, args);
848+
}
835849
return commandLine
836850
.setExecutionStrategy(executionStrategy)
837851
.setParameterExceptionHandler(parameterExceptionHandler)
@@ -846,7 +860,8 @@ private int executeCommandLine(
846860
public void toCommandLine() {
847861
commandLine =
848862
new CommandLine(this, new BesuCommandCustomFactory(besuPluginContext))
849-
.setCaseInsensitiveEnumValuesAllowed(true);
863+
.setCaseInsensitiveEnumValuesAllowed(true)
864+
.setToggleBooleanFlags(false);
850865
}
851866

852867
@Override
@@ -938,6 +953,33 @@ private void configurePrecompileCaching() {
938953
.inc());
939954
}
940955

956+
/** Reject any option that is not multi-valued but appears more than once. */
957+
private static void rejectDuplicateScalarOptions(final ParseResult pr) {
958+
for (OptionSpec spec : pr.matchedOptions()) {
959+
960+
// skip help/version flags
961+
if (spec.usageHelp() || spec.versionHelp()) {
962+
continue;
963+
}
964+
965+
// ── determine if this option can repeat
966+
ITypeInfo type = spec.typeInfo();
967+
boolean multiValued =
968+
spec.arity().max() > 1 || type.isMultiValue() || type.isCollection() || type.isArray();
969+
970+
if (multiValued) {
971+
continue; // lists are allowed to repeat
972+
}
973+
974+
// ── single-valued: abort if it appears more than once ───────────
975+
if (pr.matchedOption(spec.longestName()).stringValues().size() > 1) {
976+
throw new ParameterException(
977+
pr.commandSpec().commandLine(),
978+
String.format("Option '%s' should be specified only once", spec.longestName()));
979+
}
980+
}
981+
}
982+
941983
private void checkPermissionsAndPrintPaths(final String userName) {
942984
// Check permissions for the data path
943985
checkPermissions(dataDir(), userName, false);

app/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import static org.hyperledger.besu.plugin.services.storage.DataStorageFormat.BONSAI;
4040
import static org.junit.jupiter.api.Assumptions.assumeTrue;
4141
import static org.mockito.ArgumentMatchers.any;
42+
import static org.mockito.ArgumentMatchers.argThat;
4243
import static org.mockito.ArgumentMatchers.contains;
4344
import static org.mockito.ArgumentMatchers.eq;
4445
import static org.mockito.ArgumentMatchers.isNotNull;
@@ -2770,4 +2771,21 @@ void chainPruningEnabledWithPOA() throws IOException {
27702771
"--version-compatibility-protection=false");
27712772
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
27722773
}
2774+
2775+
@Test
2776+
public void shouldLogErrorIfDuplicateBooleanOptionUsed() {
2777+
parseCommand("--p2p-enabled=true", "--p2p-enabled=false");
2778+
assertThat(commandErrorOutput.toString(UTF_8))
2779+
.contains("Option '--p2p-enabled' should be specified only once");
2780+
}
2781+
2782+
@Test
2783+
public void shouldNotWarnForRepeatableOptionBootnodes() {
2784+
parseCommand(
2785+
"enode://" + VALID_NODE_ID + "@192.168.0.1:4567",
2786+
"enode://" + VALID_NODE_ID + "@192.168.0.2:4567");
2787+
2788+
// Verify that the logger does NOT warn about duplication
2789+
verify(mockLogger, never()).warn(contains("bootnodes"));
2790+
}
27732791
}

0 commit comments

Comments
 (0)