|
85 | 85 | import static org.elasticsearch.xpack.esql.IdentifierGenerator.Features.DATE_MATH; |
86 | 86 | import static org.elasticsearch.xpack.esql.IdentifierGenerator.Features.INDEX_SELECTOR; |
87 | 87 | import static org.elasticsearch.xpack.esql.IdentifierGenerator.Features.WILDCARD_PATTERN; |
| 88 | +import static org.elasticsearch.xpack.esql.IdentifierGenerator.quote; |
88 | 89 | import static org.elasticsearch.xpack.esql.IdentifierGenerator.randomIndexPattern; |
89 | 90 | import static org.elasticsearch.xpack.esql.IdentifierGenerator.randomIndexPatterns; |
90 | 91 | import static org.elasticsearch.xpack.esql.IdentifierGenerator.unquoteIndexPattern; |
@@ -693,20 +694,19 @@ public void testInvalidCharacterInIndexPattern() { |
693 | 694 | } |
694 | 695 | lineNumber = command.contains("FROM") ? "line 1:9: " : "line 1:12: "; |
695 | 696 | String indexStarLineNumber = command.contains("FROM") ? "line 1:14: " : "line 1:17: "; |
696 | | - clustersAndIndices(command, "*", "-index#pattern"); |
697 | | - clustersAndIndices(command, "index*", "-index#pattern"); |
698 | | - clustersAndIndices(command, "*", "-<--logstash-{now/M{yyyy.MM}}>"); |
699 | | - clustersAndIndices(command, "index*", "-<--logstash#-{now/M{yyyy.MM}}>"); |
| 697 | + expectInvalidIndexNameErrorWithLineNumber(command, "*, index#pattern", lineNumber, "index#pattern", "must not contain '#'"); |
| 698 | + expectInvalidIndexNameErrorWithLineNumber( |
| 699 | + command, |
| 700 | + "index*, index#pattern", |
| 701 | + indexStarLineNumber, |
| 702 | + "index#pattern", |
| 703 | + "must not contain '#'" |
| 704 | + ); |
700 | 705 | expectDateMathErrorWithLineNumber(command, "*, \"-<-logstash-{now/D}>\"", lineNumber, dateMathError); |
701 | 706 | expectDateMathErrorWithLineNumber(command, "*, -<-logstash-{now/D}>", lineNumber, dateMathError); |
702 | 707 | expectDateMathErrorWithLineNumber(command, "\"*, -<-logstash-{now/D}>\"", commands.get(command), dateMathError); |
703 | 708 | expectDateMathErrorWithLineNumber(command, "\"*, -<-logst:ash-{now/D}>\"", commands.get(command), dateMathError); |
704 | 709 | if (EsqlCapabilities.Cap.INDEX_COMPONENT_SELECTORS.isEnabled()) { |
705 | | - clustersAndIndices(command, "*", "-index#pattern::data"); |
706 | | - clustersAndIndices(command, "*", "-index#pattern::data"); |
707 | | - clustersAndIndices(command, "index*", "-index#pattern::data"); |
708 | | - clustersAndIndices(command, "*", "-<--logstash-{now/M{yyyy.MM}}>::data"); |
709 | | - clustersAndIndices(command, "index*", "-<--logstash#-{now/M{yyyy.MM}}>::data"); |
710 | 710 | // Throw on invalid date math |
711 | 711 | expectDateMathErrorWithLineNumber(command, "*, \"-<-logstash-{now/D}>\"::data", lineNumber, dateMathError); |
712 | 712 | expectDateMathErrorWithLineNumber(command, "*, -<-logstash-{now/D}>::data", lineNumber, dateMathError); |
@@ -3108,6 +3108,90 @@ public void testValidJoinPattern() { |
3108 | 3108 | assertThat(joinType.coreJoin().joinName(), equalTo("LEFT OUTER")); |
3109 | 3109 | } |
3110 | 3110 |
|
| 3111 | + public void testInvalidPatternsWithIntermittentQuotes() { |
| 3112 | + // There are 3 ways of crafting an invalid index pattern that conforms to the grammar defined through ANTLR. |
| 3113 | + // 1. Quoting the entire pattern. |
| 3114 | + // 2. Quoting the cluster alias - "invalid cluster alias":<rest of the pattern> |
| 3115 | + // 3. Quoting the index name - <cluster alias>:"invalid index" |
| 3116 | + |
| 3117 | + // Prohibited char in a quoted cross cluster index pattern should result in an error. |
| 3118 | + { |
| 3119 | + var firstValidIndexPattern = randomIndexPattern(); |
| 3120 | + // Exclude date math expressions or its parsing might result in an error when an invalid char is sneaked in. |
| 3121 | + var secondRandomIndex = unquoteIndexPattern(randomIndexPattern(without(DATE_MATH), without(INDEX_SELECTOR))); |
| 3122 | + |
| 3123 | + // Select an invalid char to sneak in. |
| 3124 | + char[] invalidChars = { ' ', '\"', '*', ',', '/', '<', '>', '?', '|' }; |
| 3125 | + var randomInvalidChar = invalidChars[randomIntBetween(0, invalidChars.length - 1)]; |
| 3126 | + |
| 3127 | + // Find a random position to insert the invalid char. |
| 3128 | + var randomPos = randomIntBetween(3, secondRandomIndex.length() - 1); |
| 3129 | + // Construct the new invalid index pattern. |
| 3130 | + var remoteIndexWithInvalidChar = quote( |
| 3131 | + secondRandomIndex.substring(0, randomPos) + randomInvalidChar + secondRandomIndex.substring(randomPos + 1) |
| 3132 | + ); |
| 3133 | + |
| 3134 | + var query = "FROM " + firstValidIndexPattern + "," + remoteIndexWithInvalidChar; |
| 3135 | + expectError(query, "must not contain the following characters [' ','\"','*',',','/','<','>','?','\\','|']"); |
| 3136 | + } |
| 3137 | + |
| 3138 | + // Cluster names with invalid characters, i.e. ':' should result in an error. |
| 3139 | + { |
| 3140 | + var randomIndex = randomIndexPattern(); |
| 3141 | + |
| 3142 | + // In the form of: "*|cluster alias:random string". |
| 3143 | + var malformedClusterAlias = quote((randomBoolean() ? "*" : randomIdentifier()) + ":" + randomIdentifier()); |
| 3144 | + |
| 3145 | + // We do not generate a cross cluster pattern or else we'd be getting a different error (which is tested in |
| 3146 | + // the next test). |
| 3147 | + var remoteIndex = randomIndexPattern(without(CROSS_CLUSTER)); |
| 3148 | + // Format: FROM <some index>, "<cluster alias: random string>":<remote index> |
| 3149 | + var query = "FROM " + randomIndex + "," + malformedClusterAlias + ":" + remoteIndex; |
| 3150 | + expectError(query, "cluster string [" + unquoteIndexPattern(malformedClusterAlias) + "] must not contain ':'"); |
| 3151 | + } |
| 3152 | + |
| 3153 | + // If a remote index is quoted and has a remote cluster alias and the pattern is already prefixed with |
| 3154 | + // a cluster alias, we should flag the cluster alias in the pattern. |
| 3155 | + { |
| 3156 | + var randomIndex = randomIndexPattern(); |
| 3157 | + var remoteClusterAlias = randomBoolean() ? "*" : randomIdentifier(); |
| 3158 | + // In the form of: random string:random string. |
| 3159 | + var malformedRemoteIndex = quote(unquoteIndexPattern(randomIndexPattern(CROSS_CLUSTER))); |
| 3160 | + // Format: FROM <some index>, <cluster alias>:"random string:random string" |
| 3161 | + var query = "FROM " + randomIndex + "," + remoteClusterAlias + ":" + malformedRemoteIndex; |
| 3162 | + expectError(query, "contains a cluster alias despite specifying one"); |
| 3163 | + } |
| 3164 | + |
| 3165 | + if (EsqlCapabilities.Cap.INDEX_COMPONENT_SELECTORS.isEnabled()) { |
| 3166 | + // If a stream in on a remote and the pattern is entirely quoted, we should be able to validate it. |
| 3167 | + // Note: invalid selector syntax is covered in a different test. |
| 3168 | + { |
| 3169 | + var fromPattern = randomIndexPattern(); |
| 3170 | + var malformedIndexSelectorPattern = quote( |
| 3171 | + (randomIdentifier()) + ":" + unquoteIndexPattern(randomIndexPattern(INDEX_SELECTOR, without(CROSS_CLUSTER))) |
| 3172 | + ); |
| 3173 | + // Format: FROM <some index>, "<cluster alias>:<some index>::<data|failures>" |
| 3174 | + var query = "FROM " + fromPattern + "," + malformedIndexSelectorPattern; |
| 3175 | + expectError(query, "Selectors are not yet supported on remote cluster patterns"); |
| 3176 | + } |
| 3177 | + |
| 3178 | + // If a stream in on a remote and the cluster alias and index pattern are separately quoted, we should |
| 3179 | + // still be able to validate it. |
| 3180 | + // Note: |
| 3181 | + // 1. Invalid selector syntax is covered in a different test. |
| 3182 | + // 2. We unquote a pattern and re-quote it to prevent partial quoting of pattern fragments. |
| 3183 | + { |
| 3184 | + var fromPattern = randomIndexPattern(); |
| 3185 | + var malformedIndexSelectorPattern = quote(randomIdentifier()) |
| 3186 | + + ":" |
| 3187 | + + quote(unquoteIndexPattern(randomIndexPattern(INDEX_SELECTOR, without(CROSS_CLUSTER)))); |
| 3188 | + // Format: FROM <some index>, "<cluster alias>":"<some index>::<data|failures>" |
| 3189 | + var query = "FROM " + fromPattern + "," + malformedIndexSelectorPattern; |
| 3190 | + expectError(query, "Selectors are not yet supported on remote cluster patterns"); |
| 3191 | + } |
| 3192 | + } |
| 3193 | + } |
| 3194 | + |
3111 | 3195 | public void testInvalidJoinPatterns() { |
3112 | 3196 | assumeTrue("LOOKUP JOIN requires corresponding capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); |
3113 | 3197 |
|
|
0 commit comments