From 4e07979ccca556d5f05a1be05b88f0bd85ff3f66 Mon Sep 17 00:00:00 2001 From: Zipeng WU Date: Fri, 11 Dec 2020 11:53:25 +0100 Subject: [PATCH] Refactor regex that can lead to a stack overflow for large inputs --- .../sonar/api/server/rule/RuleParamType.java | 26 ++++++++++++++++--- .../api/server/rule/RuleParamTypeTest.java | 6 ++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java index dd9c4d8409c..563369008ca 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java @@ -19,6 +19,7 @@ */ package org.sonar.api.server.rule; +import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; @@ -40,7 +41,6 @@ public final class RuleParamType { public static final RuleParamType INTEGER = new RuleParamType("INTEGER"); public static final RuleParamType FLOAT = new RuleParamType("FLOAT"); - private static final String CSV_SPLIT_REGEX = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"; private static final String VALUES_PARAM = "values"; private static final String MULTIPLE_PARAM = "multiple"; private static final String PARAMETER_SEPARATOR = "="; @@ -110,7 +110,6 @@ public final class RuleParamType { return new RuleParamType(type, true, acceptedValues); } - // TODO validate format public static RuleParamType parse(String s) { // deprecated formats if ("i".equals(s) || "i{}".equals(s)) { @@ -131,7 +130,7 @@ public final class RuleParamType { String format = StringUtils.substringBefore(s, OPTION_SEPARATOR); String values = null; boolean multiple = false; - String[] options = s.split(CSV_SPLIT_REGEX); + String[] options = csvFormatSplit(s); for (String option : options) { String opt = StringEscapeUtils.unescapeCsv(option); if (opt.startsWith(VALUES_PARAM + PARAMETER_SEPARATOR)) { @@ -143,7 +142,26 @@ public final class RuleParamType { if (values == null || StringUtils.isBlank(values)) { return new RuleParamType(format); } - return new RuleParamType(format, multiple, values.split(CSV_SPLIT_REGEX)); + return new RuleParamType(format, multiple, csvFormatSplit(values)); + } + + private static String[] csvFormatSplit(String input) { + List result = new ArrayList<>(); + boolean betweenQuote = false; + int startIndex = 0; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '"') { + betweenQuote = !betweenQuote; + } else if (!betweenQuote && c == ',') { + result.add(input.substring(startIndex, i)); + startIndex = i + 1; + } + } + if (startIndex < input.length()) { + result.add(input.substring(startIndex)); + } + return result.toArray(new String[0]); } @Override diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java index e45d7011e0e..99069e38cae 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleParamTypeTest.java @@ -68,13 +68,13 @@ public class RuleParamTypeTest { assertThat(selectList.multiple()).isFalse(); assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST,values=\"foo,bar,\""); - RuleParamType.parse("SINGLE_SELECT_LIST,values=\"foo,bar\",multiple=false"); + selectList = RuleParamType.parse("SINGLE_SELECT_LIST,values=\"foo,bar\",multiple=false"); assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST"); assertThat(selectList.values()).containsOnly("foo", "bar"); assertThat(selectList.multiple()).isFalse(); assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST,values=\"foo,bar,\""); - RuleParamType.parse("SINGLE_SELECT_LIST,\"values=foo,bar\",\"multiple=false\""); + selectList = RuleParamType.parse("SINGLE_SELECT_LIST,\"values=foo,bar\",multiple=false"); assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST"); assertThat(selectList.values()).containsOnly("foo", "bar"); assertThat(selectList.multiple()).isFalse(); @@ -96,7 +96,7 @@ public class RuleParamTypeTest { assertThat(selectList.multiple()).isTrue(); assertThat(selectList.toString()).isEqualTo("SINGLE_SELECT_LIST,multiple=true,values=\"foo,bar,\""); - RuleParamType.parse("SINGLE_SELECT_LIST,\"values=foo,bar\",\"multiple=true\""); + selectList = RuleParamType.parse("SINGLE_SELECT_LIST,\"values=foo,bar\",multiple=true"); assertThat(selectList.type()).isEqualTo("SINGLE_SELECT_LIST"); assertThat(selectList.values()).containsOnly("foo", "bar"); assertThat(selectList.multiple()).isTrue(); -- 2.39.5