Skip to content

Commit 781d29c

Browse files
Katrixdualspiral
authored andcommitted
Fix DoS exploit in PatternMatchingCommandElement
1 parent b70499c commit 781d29c

File tree

2 files changed

+42
-16
lines changed

2 files changed

+42
-16
lines changed

src/main/java/org/spongepowered/api/command/args/GenericArguments.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1542,7 +1542,12 @@ public List<String> complete(CommandSource src, CommandArgs args, CommandContext
15421542
Iterable<String> choices = getCompletionChoices(src);
15431543
final Optional<String> nextArg = args.nextIfPresent();
15441544
if (nextArg.isPresent()) {
1545-
choices = Iterables.filter(choices, input -> getFormattedPattern(nextArg.get()).matcher(input).find());
1545+
if (useRegex) {
1546+
choices = Iterables.filter(choices, input -> getFormattedPattern(nextArg.get()).matcher(input).find());
1547+
} else {
1548+
String arg = nextArg.get();
1549+
choices = Iterables.filter(choices, input -> input.regionMatches(true, 0, arg, 0, arg.length()));
1550+
}
15461551
}
15471552
return ImmutableList.copyOf(choices);
15481553
}

src/main/java/org/spongepowered/api/command/args/PatternMatchingCommandElement.java

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,47 @@
4343
*/
4444
public abstract class PatternMatchingCommandElement extends CommandElement {
4545
private static final Text nullKeyArg = t("argument");
46+
final boolean useRegex;
4647

47-
protected PatternMatchingCommandElement(@Nullable Text key) {
48+
/**
49+
* @param yesIWantRegex Specify if you want to allow regex for users of
50+
* the command element. Note that this will open up for DoS attacks.
51+
*/
52+
protected PatternMatchingCommandElement(@Nullable Text key, boolean yesIWantRegex) {
4853
super(key);
54+
this.useRegex = yesIWantRegex;
55+
}
56+
57+
protected PatternMatchingCommandElement(@Nullable Text key) {
58+
this(key, false);
4959
}
5060

5161
@Nullable
5262
@Override
5363
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
54-
final String unformattedPattern = args.next();
5564
Iterable<String> choices = getChoices(source);
56-
57-
// Check for an exact match before we create the regex.
58-
// We do this because anything with ^[abc] would not match [abc]
59-
Optional<Object> exactMatch = getExactMatch(choices, unformattedPattern);
60-
if (exactMatch.isPresent()) {
61-
// Return this as a collection as this can get transformed by the subclass.
62-
return Collections.singleton(exactMatch.get());
65+
Iterable<Object> ret;
66+
String arg = args.next();
67+
68+
if (useRegex) {
69+
// Check for an exact match before we create the regex.
70+
// We do this because anything with ^[abc] would not match [abc]
71+
Optional<Object> exactMatch = getExactMatch(choices, arg);
72+
if (exactMatch.isPresent()) {
73+
// Return this as a collection as this can get transformed by the subclass.
74+
return Collections.singleton(exactMatch.get());
75+
}
76+
77+
Pattern pattern = getFormattedPattern(arg);
78+
ret = Iterables.transform(Iterables.filter(choices, element -> pattern.matcher(element).find()), this::getValue);
79+
} else {
80+
Iterable<String> startsWith = Iterables.filter(choices, element -> element.regionMatches(true, 0, arg, 0, arg.length()));
81+
ret = Iterables.transform(startsWith, this::getValue);
6382
}
6483

65-
Pattern pattern = getFormattedPattern(unformattedPattern);
66-
Iterable<Object> ret = Iterables.transform(Iterables.filter(choices, element -> pattern.matcher(element).find()), this::getValue);
67-
6884
if (!ret.iterator().hasNext()) {
69-
throw args.createError(t("No values matching pattern '%s' present for %s!", unformattedPattern, getKey() == null
70-
? nullKeyArg : getKey()));
85+
throw args.createError(t("No values matching pattern '%s' present for %s!", arg, getKey() == null
86+
? nullKeyArg : getKey()));
7187
}
7288
return ret;
7389
}
@@ -77,7 +93,12 @@ public List<String> complete(CommandSource src, CommandArgs args, CommandContext
7793
Iterable<String> choices = getChoices(src);
7894
final Optional<String> nextArg = args.nextIfPresent();
7995
if (nextArg.isPresent()) {
80-
choices = Iterables.filter(choices, input -> getFormattedPattern(nextArg.get()).matcher(input).find());
96+
if (useRegex) {
97+
choices = Iterables.filter(choices, input -> getFormattedPattern(nextArg.get()).matcher(input).find());
98+
} else {
99+
String arg = nextArg.get();
100+
choices = Iterables.filter(choices, input -> input.regionMatches(true, 0, arg, 0, arg.length()));
101+
}
81102
}
82103
return ImmutableList.copyOf(choices);
83104
}

0 commit comments

Comments
 (0)