Searching Opencast may cause a denial of service
Description
Opencast is free and open source software for automated video capture and distribution. First noticed in Opencast 13 and 14, Opencast's Elasticsearch integration may generate syntactically invalid Elasticsearch queries in relation to previously acceptable search queries. From Opencast version 11.4 and newer, Elasticsearch queries are retried a configurable number of times in the case of error to handle temporary losses of connection to Elasticsearch. These invalid queries would fail, causing the retry mechanism to begin requerying with the same syntactically invalid query immediately, in an infinite loop. This causes a massive increase in log size which can in some cases cause a denial of service due to disk exhaustion.
Opencast 13.10 and Opencast 14.3 contain patches which address the base issue, with Opencast 16.7 containing changes which harmonize the search behaviour between the admin UI and external API. Users are strongly recommended to upgrade as soon as possible if running versions prior to 13.10 or 14.3. While the relevant endpoints require (by default) ROLE_ADMIN or ROLE_API_SERIES_VIEW, the problem queries are otherwise innocuous. This issue could be easily triggered by normal administrative work on an affected Opencast system. Those who run a version newer than 13.10 and 14.3 and see different results when searching in their admin UI vs your external API or LMS, may resolve the issue by upgrading to 16.7. No known workarounds for the vulnerability are available.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.opencastproject:opencast-elasticsearch-implMaven | >= 11.4, < 13.10 | 13.10 |
org.opencastproject:opencast-elasticsearch-implMaven | >= 14.0, < 14.3 | 14.3 |
org.opencastproject:opencast-elasticsearch-implMaven | >= 15.0, < 16.7 | 16.7 |
Affected products
1Patches
13d5ebd163674Merge commit from fork
9 files changed · +173 −195
modules/admin-ui/src/main/java/org/opencastproject/adminui/endpoint/AbstractEventEndpoint.java+1 −1 modified@@ -60,7 +60,6 @@ import org.opencastproject.adminui.exception.JobEndpointException; import org.opencastproject.adminui.impl.AdminUIConfiguration; import org.opencastproject.adminui.util.BulkUpdateUtil; -import org.opencastproject.adminui.util.QueryPreprocessor; import org.opencastproject.assetmanager.api.AssetManager; import org.opencastproject.authorization.xacml.manager.api.AclService; import org.opencastproject.authorization.xacml.manager.api.ManagedAcl; @@ -72,6 +71,7 @@ import org.opencastproject.elasticsearch.api.SearchResult; import org.opencastproject.elasticsearch.api.SearchResultItem; import org.opencastproject.elasticsearch.index.ElasticsearchIndex; +import org.opencastproject.elasticsearch.index.QueryPreprocessor; import org.opencastproject.elasticsearch.index.objects.event.Event; import org.opencastproject.elasticsearch.index.objects.event.EventIndexSchema; import org.opencastproject.elasticsearch.index.objects.event.EventSearchQuery;
modules/admin-ui/src/main/java/org/opencastproject/adminui/endpoint/SeriesEndpoint.java+1 −1 modified@@ -55,7 +55,6 @@ import org.opencastproject.adminui.impl.AdminUIConfiguration; import org.opencastproject.adminui.tobira.TobiraException; import org.opencastproject.adminui.tobira.TobiraService; -import org.opencastproject.adminui.util.QueryPreprocessor; import org.opencastproject.authorization.xacml.manager.api.AclService; import org.opencastproject.authorization.xacml.manager.api.AclServiceFactory; import org.opencastproject.authorization.xacml.manager.api.ManagedAcl; @@ -64,6 +63,7 @@ import org.opencastproject.elasticsearch.api.SearchResult; import org.opencastproject.elasticsearch.api.SearchResultItem; import org.opencastproject.elasticsearch.index.ElasticsearchIndex; +import org.opencastproject.elasticsearch.index.QueryPreprocessor; import org.opencastproject.elasticsearch.index.objects.event.Event; import org.opencastproject.elasticsearch.index.objects.event.EventSearchQuery; import org.opencastproject.elasticsearch.index.objects.series.Series;
modules/admin-ui/src/main/java/org/opencastproject/adminui/endpoint/ThemesEndpoint.java+1 −1 modified@@ -38,11 +38,11 @@ import static org.opencastproject.index.service.util.RestUtils.okJsonList; import static org.opencastproject.util.doc.rest.RestParameter.Type.STRING; -import org.opencastproject.adminui.util.QueryPreprocessor; import org.opencastproject.elasticsearch.api.SearchIndexException; import org.opencastproject.elasticsearch.api.SearchResult; import org.opencastproject.elasticsearch.api.SearchResultItem; import org.opencastproject.elasticsearch.index.ElasticsearchIndex; +import org.opencastproject.elasticsearch.index.QueryPreprocessor; import org.opencastproject.elasticsearch.index.objects.series.Series; import org.opencastproject.elasticsearch.index.objects.series.SeriesSearchQuery; import org.opencastproject.elasticsearch.index.objects.theme.IndexTheme;
modules/admin-ui/src/test/java/org/opencastproject/adminui/util/QueryPreprocessorTest.java+0 −146 removed@@ -1,146 +0,0 @@ -/* - * Licensed to The Apereo Foundation under one or more contributor license - * agreements. See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * - * The Apereo Foundation licenses this file to you under the Educational - * Community License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License - * at: - * - * http://opensource.org/licenses/ecl2.txt - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - * - */ - -package org.opencastproject.adminui.util; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class QueryPreprocessorTest { - - @Test - public void testBlankQueries() { - assertEquals("", QueryPreprocessor.sanitize("")); - assertEquals("", QueryPreprocessor.sanitize(" ")); - } - - @Test - public void testDoubleQuotes() { - // No partial matching for quoted strings - assertEquals("\"Hello\"", QueryPreprocessor.sanitize("\"Hello\"")); - assertEquals("*Hello* \"World\"", QueryPreprocessor.sanitize("Hello \"World\"")); - - // Auto-completion in case of missing double-quote - assertEquals("*Hello* \"World\"", QueryPreprocessor.sanitize("Hello \"World")); - assertEquals("*Hello* \"World Again\"", QueryPreprocessor.sanitize("Hello \"World Again")); - - // Escape double quote within tokens, i.e. ensure whitespace separated tokens - assertEquals("*He\\\"llo* *Wor\\\"ld*", QueryPreprocessor.sanitize("He\"llo Wor\"ld")); - } - - @Test - public void testWildcards() { - // Don't escape wildcards occuring as individual tokens - assertEquals("*", QueryPreprocessor.sanitize("*")); - assertEquals("*", QueryPreprocessor.sanitize(" * ")); - assertEquals("*?*", QueryPreprocessor.sanitize("?")); - assertEquals("*?*", QueryPreprocessor.sanitize(" ? ")); - - // Don't escape wildcards occuring within tokens - assertEquals("*H*llo* *Worl*d*", QueryPreprocessor.sanitize("H*llo Worl*d")); - assertEquals("*H?llo* *Worl?d*", QueryPreprocessor.sanitize("H?llo Worl?d")); - } - - @Test - public void testCharacterEscaping() { - // Unsupported special characters are escaped - assertEquals("*\\(* *\\)* *\\[* *\\]* *\\{* *\\}* *\\~* *\\^* *\\:* *\\\\*", QueryPreprocessor.sanitize(" ( ) [ ] { } ~ ^ : \\")); - - // Unsupported special characters are not escaped in quoted strings - assertEquals("\" ( ) [ ] { } ~ ^ : \\\"", QueryPreprocessor.sanitize("\" ( ) [ ] { } ~ ^ : \\\"")); - - // Supported special characters are not escape in quoted strings - assertEquals("\"|| && + - ! * ?\"", QueryPreprocessor.sanitize("\"|| && + - ! * ?\"")); - } - - @Test - public void testUnaryOperators() { - testUnaryOperator("+"); - testUnaryOperator("-"); - testUnaryOperator("!"); - } - - private void testUnaryOperator(String operator) { - // Escape operator if operand is missing - assertEquals("\\" + operator, QueryPreprocessor.sanitize(operator)); - assertEquals("\\" + operator, QueryPreprocessor.sanitize(" " + operator + " ")); - - // Escape operator if occuring within a token - assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("test" + operator + "unit")); - assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("*test" + operator + "unit")); - assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("test" + operator + "unit*")); - assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("*test" + operator + "unit*")); - assertEquals("*test\\" + operator + "unit\\" + operator + "*", - QueryPreprocessor.sanitize("test" + operator + "unit" + operator)); - assertEquals( - operator + "*\\" + operator + "test\\" + operator + "unit\\" + operator + "\\" + operator + "*", - QueryPreprocessor.sanitize(operator + operator + "test" + operator + "unit" + operator + operator)); - - // Partial matching for operands - assertEquals(operator + "*test\\" + operator + "unit*", - QueryPreprocessor.sanitize(operator + "test" + operator + "unit")); - assertEquals(operator + "*test\\" + operator + "unit*", - QueryPreprocessor.sanitize(operator + "*test" + operator + "unit")); - assertEquals(operator + "*test\\" + operator + "unit*", - QueryPreprocessor.sanitize(operator + "test" + operator + "unit*")); - assertEquals(operator + "*test\\" + operator + "unit*", - QueryPreprocessor.sanitize(operator + "*test" + operator + "unit*")); - - // Binary operators are escaped when appearing as operangs of unary operators - assertEquals(operator + "*\\|\\|*", QueryPreprocessor.sanitize(operator + "||")); - assertEquals(operator + "*\\&\\&*", QueryPreprocessor.sanitize(operator + "&&")); - - // Double quotes can be used in argument - assertEquals(operator + "\"Hello World\"", QueryPreprocessor.sanitize(operator + "\"Hello World\"")); - } - - @Test - public void testBinaryOperators() { - testBinaryOperator("&&"); - testBinaryOperator("||"); - } - - private void testBinaryOperator(String operator) { - // Escape operator if operands are missing - assertEquals("\\" + operator, QueryPreprocessor.sanitize(operator)); - assertEquals("\\" + operator, QueryPreprocessor.sanitize(" " + operator + " ")); - assertEquals("*Hello* \\" + operator, QueryPreprocessor.sanitize("Hello " + operator)); - assertEquals("\\" + operator + " *World*", QueryPreprocessor.sanitize(operator + " World")); - - // Don't escape operator if used correctly - assertEquals("*Hello* " + operator + " *World*", - QueryPreprocessor.sanitize("Hello " + operator + " World")); - } - - @Test - public void testPartialMatches() { - assertEquals("*Hello*", QueryPreprocessor.sanitize("Hello")); - assertEquals("*Hello*", QueryPreprocessor.sanitize("*Hello")); - assertEquals("*Hello*", QueryPreprocessor.sanitize("Hello*")); - assertEquals("*Hello*", QueryPreprocessor.sanitize("*Hello*")); - assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("Hello World")); - assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("Hello* World")); - assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("Hello *World")); - assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("*Hello* *World*")); - } - -}
modules/elasticsearch-index/src/main/java/org/opencastproject/elasticsearch/index/ElasticsearchIndex.java+0 −23 modified@@ -62,7 +62,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.function.Function; @@ -840,26 +839,4 @@ private SearchResult<IndexTheme> getByQuery(ThemeSearchQuery query, int maxRetry throw new SearchIndexException("Error querying theme index", t); } } - - /** - * Escapes all reserved Elasticsearch characters in a given string - * Useful for when a given query string does not know about Elasticsearch query syntax - * @param text String to escape reserved characters in - * @return the given string with escaped characters - */ - public String escapeQuery(String text) { - Set<Character> specialChars = Set.of('\\', '+', '-', '!', '(', ')', ':', '^', '[', ']', '\"', '{', '}', '~', '*', - '?', '|', '&', '/'); - - StringBuilder sb = new StringBuilder(text.length()); - - for (char c : text.toCharArray()) { - if (specialChars.contains(c)) { - sb.append('\\'); - } - sb.append(c); - } - - return sb.toString(); - } }
modules/elasticsearch-index/src/main/java/org/opencastproject/elasticsearch/index/QueryPreprocessor.java+21 −20 renamed@@ -19,7 +19,7 @@ * */ -package org.opencastproject.adminui.util; +package org.opencastproject.elasticsearch.index; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,25 +49,25 @@ public final class QueryPreprocessor { private static final char MINUS = '-'; private static final char PLUS = '+'; private static final char ASTERISK = '*'; - private static final char EXPLANATION_MARK = '!'; + private static final char EXCLAMATION_MARK = '!'; private static final char BACKSLASH = '\\'; private static final char AMPERSAND = '&'; private static final char PIPE = '|'; private static final Set<Character> ESCAPED_CHARACTERS = new HashSet<Character>(Arrays.asList( - MINUS, - PLUS, - EXPLANATION_MARK, - BACKSLASH, - AMPERSAND, - PIPE, - '(', ')', '{', '}', '[', ']', ':', '^', '~' + MINUS, + PLUS, + EXCLAMATION_MARK, + BACKSLASH, + AMPERSAND, + PIPE, + '(', ')', '{', '}', '[', ']', ':', '^', '~', '\"', '/' )); private static final Set<Character> UNARY_OPERATORS = new HashSet<Character>(Arrays.asList( - MINUS, - PLUS, - EXPLANATION_MARK + MINUS, + PLUS, + EXCLAMATION_MARK )); private static final Set<String> BINARY_OPERATORS = new HashSet<String>(Arrays.asList("&&", "||")); @@ -95,7 +95,8 @@ public static String sanitize(String query) { if (isUnaryOperator(token)) { sanitizedToken = sanitizeUnaryOperator(token); } else if (isBinaryOperator(token)) { - if ((i == 0) || isBinaryOperator(tokens.get(i - 1)) || (i >= tokens.size() - 1) || isBinaryOperator(tokens.get(i + 1))) { + if ((i == 0) || isBinaryOperator(tokens.get(i - 1)) + || (i >= tokens.size() - 1) || isBinaryOperator(tokens.get(i + 1))) { // Escape operator since operands missing sanitizedToken = "" + BACKSLASH + token; } else { @@ -201,8 +202,8 @@ private static ArrayList<String> tokenize(String query) { tokens.add(currentToken); currentToken = ""; openDoubleQuote = false; - } else if (currentToken.isEmpty() - || (isUnaryOperator("" + charAt(i - 1, query)) && Character.isWhitespace(charAt(i - 2, query)))) { + } else if (currentToken.isEmpty() || (isUnaryOperator("" + charAt(i - 1, query)) + && Character.isWhitespace(charAt(i - 2, query)))) { currentToken += DOUBLE_QUOTE; openDoubleQuote = true; } else { @@ -216,11 +217,11 @@ private static ArrayList<String> tokenize(String query) { // We only allow unary operators as first character of a token currentToken += ch; } else if (isBinaryOperator("" + ch + charAt(i + 1, query)) - && Character.isWhitespace(charAt(i - 1, query)) - && Character.isWhitespace(charAt(i + 2, query))) { - // Binary operator detected, i.e. whitespace delimited && or || - tokens.add("" + ch + ch); - i++; // We nastily skip the binary operator, i.e. we are taken two characters in this round + && Character.isWhitespace(charAt(i - 1, query)) + && Character.isWhitespace(charAt(i + 2, query))) { + // Binary operator detected, i.e. whitespace delimited && or || + tokens.add("" + ch + ch); + i++; // We nastily skip the binary operator, i.e. we are taken two characters in this round } else if (Character.isWhitespace(ch)) { // Whitespace delimits tokens if (!currentToken.isEmpty()) {
modules/elasticsearch-index/src/test/java/org/opencastproject/elasticsearch/index/QueryPreprocessorTest.java+144 −0 added@@ -0,0 +1,144 @@ +/* + * Licensed to The Apereo Foundation under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * + * The Apereo Foundation licenses this file to you under the Educational + * Community License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License + * at: + * + * http://opensource.org/licenses/ecl2.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + */ + +package org.opencastproject.elasticsearch.index; + +import org.junit.Assert; +import org.junit.Test; + +public class QueryPreprocessorTest { + + @Test + public void testBlankQueries() { + Assert.assertEquals("", QueryPreprocessor.sanitize("")); + Assert.assertEquals("", QueryPreprocessor.sanitize(" ")); + } + + @Test + public void testDoubleQuotes() { + // No partial matching for quoted strings + Assert.assertEquals("\"Hello\"", QueryPreprocessor.sanitize("\"Hello\"")); + Assert.assertEquals("*Hello* \"World\"", QueryPreprocessor.sanitize("Hello \"World\"")); + + // Auto-completion in case of missing double-quote + Assert.assertEquals("*Hello* \"World\"", QueryPreprocessor.sanitize("Hello \"World")); + Assert.assertEquals("*Hello* \"World Again\"", QueryPreprocessor.sanitize("Hello \"World Again")); + + // Escape double quote within tokens, i.e. ensure whitespace separated tokens + Assert.assertEquals("*He\\\"llo* *Wor\\\"ld*", QueryPreprocessor.sanitize("He\"llo Wor\"ld")); + } + + @Test + public void testWildcards() { + // Don't escape wildcards occuring as individual tokens + Assert.assertEquals("*", QueryPreprocessor.sanitize("*")); + Assert.assertEquals("*", QueryPreprocessor.sanitize(" * ")); + Assert.assertEquals("*?*", QueryPreprocessor.sanitize("?")); + Assert.assertEquals("*?*", QueryPreprocessor.sanitize(" ? ")); + + // Don't escape wildcards occuring within tokens + Assert.assertEquals("*H*llo* *Worl*d*", QueryPreprocessor.sanitize("H*llo Worl*d")); + Assert.assertEquals("*H?llo* *Worl?d*", QueryPreprocessor.sanitize("H?llo Worl?d")); + } + + @Test + public void testCharacterEscaping() { + // Unsupported special characters are escaped + Assert.assertEquals("*\\(* *\\)* *\\[* *\\]* *\\{* *\\}* *\\~* *\\^* *\\:* *\\\\*", + QueryPreprocessor.sanitize(" ( ) [ ] { } ~ ^ : \\")); + + // Unsupported special characters are not escaped in quoted strings + Assert.assertEquals("\" ( ) [ ] { } ~ ^ : \\\"", QueryPreprocessor.sanitize("\" ( ) [ ] { } ~ ^ : \\\"")); + + // Supported special characters are not escape in quoted strings + Assert.assertEquals("\"|| && + - ! * ?\"", QueryPreprocessor.sanitize("\"|| && + - ! * ?\"")); + } + + @Test + public void testUnaryOperators() { + testUnaryOperator("+"); + testUnaryOperator("-"); + testUnaryOperator("!"); + } + + private void testUnaryOperator(String operator) { + // Escape operator if operand is missing + Assert.assertEquals("\\" + operator, QueryPreprocessor.sanitize(operator)); + Assert.assertEquals("\\" + operator, QueryPreprocessor.sanitize(" " + operator + " ")); + + // Escape operator if occuring within a token + Assert.assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("test" + operator + "unit")); + Assert.assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("*test" + operator + "unit")); + Assert.assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("test" + operator + "unit*")); + Assert.assertEquals("*test\\" + operator + "unit*", QueryPreprocessor.sanitize("*test" + operator + "unit*")); + Assert.assertEquals("*test\\" + operator + "unit\\" + operator + "*", + QueryPreprocessor.sanitize("test" + operator + "unit" + operator)); + Assert.assertEquals(operator + "*\\" + operator + "test\\" + operator + "unit\\" + operator + "\\" + operator + "*", + QueryPreprocessor.sanitize(operator + operator + "test" + operator + "unit" + operator + operator)); + + // Partial matching for operands + Assert.assertEquals(operator + "*test\\" + operator + "unit*", + QueryPreprocessor.sanitize(operator + "test" + operator + "unit")); + Assert.assertEquals(operator + "*test\\" + operator + "unit*", + QueryPreprocessor.sanitize(operator + "*test" + operator + "unit")); + Assert.assertEquals(operator + "*test\\" + operator + "unit*", + QueryPreprocessor.sanitize(operator + "test" + operator + "unit*")); + Assert.assertEquals(operator + "*test\\" + operator + "unit*", + QueryPreprocessor.sanitize(operator + "*test" + operator + "unit*")); + + // Binary operators are escaped when appearing as operangs of unary operators + Assert.assertEquals(operator + "*\\|\\|*", QueryPreprocessor.sanitize(operator + "||")); + Assert.assertEquals(operator + "*\\&\\&*", QueryPreprocessor.sanitize(operator + "&&")); + + // Double quotes can be used in argument + Assert.assertEquals(operator + "\"Hello World\"", QueryPreprocessor.sanitize(operator + "\"Hello World\"")); + } + + @Test + public void testBinaryOperators() { + testBinaryOperator("&&"); + testBinaryOperator("||"); + } + + private void testBinaryOperator(String operator) { + // Escape operator if operands are missing + Assert.assertEquals("\\" + operator, QueryPreprocessor.sanitize(operator)); + Assert.assertEquals("\\" + operator, QueryPreprocessor.sanitize(" " + operator + " ")); + Assert.assertEquals("*Hello* \\" + operator, QueryPreprocessor.sanitize("Hello " + operator)); + Assert.assertEquals("\\" + operator + " *World*", QueryPreprocessor.sanitize(operator + " World")); + + // Don't escape operator if used correctly + Assert.assertEquals("*Hello* " + operator + " *World*", QueryPreprocessor.sanitize("Hello " + operator + " World")); + } + + @Test + public void testPartialMatches() { + Assert.assertEquals("*Hello*", QueryPreprocessor.sanitize("Hello")); + Assert.assertEquals("*Hello*", QueryPreprocessor.sanitize("*Hello")); + Assert.assertEquals("*Hello*", QueryPreprocessor.sanitize("Hello*")); + Assert.assertEquals("*Hello*", QueryPreprocessor.sanitize("*Hello*")); + Assert.assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("Hello World")); + Assert.assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("Hello* World")); + Assert.assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("Hello *World")); + Assert.assertEquals("*Hello* *World*", QueryPreprocessor.sanitize("*Hello* *World*")); + } + +}
modules/external-api/src/main/java/org/opencastproject/external/endpoint/EventsEndpoint.java+2 −1 modified@@ -46,6 +46,7 @@ import org.opencastproject.elasticsearch.api.SearchResult; import org.opencastproject.elasticsearch.api.SearchResultItem; import org.opencastproject.elasticsearch.index.ElasticsearchIndex; +import org.opencastproject.elasticsearch.index.QueryPreprocessor; import org.opencastproject.elasticsearch.index.objects.IndexObject; import org.opencastproject.elasticsearch.index.objects.event.Event; import org.opencastproject.elasticsearch.index.objects.event.EventIndexSchema; @@ -794,7 +795,7 @@ public Response getEvents(@HeaderParam("Accept") String acceptHeader, @QueryPara } else if ("location".equals(name)) { query.withLocation(value); } else if ("textFilter".equals(name)) { - query.withText("*" + elasticsearchIndex.escapeQuery(value) + "*"); + query.withText(QueryPreprocessor.sanitize(value)); } else if ("series".equals(name)) { query.withSeriesId(value); } else if ("subject".equals(name)) {
modules/external-api/src/main/java/org/opencastproject/external/endpoint/SeriesEndpoint.java+3 −2 modified@@ -43,6 +43,7 @@ import org.opencastproject.elasticsearch.api.SearchResult; import org.opencastproject.elasticsearch.api.SearchResultItem; import org.opencastproject.elasticsearch.index.ElasticsearchIndex; +import org.opencastproject.elasticsearch.index.QueryPreprocessor; import org.opencastproject.elasticsearch.index.objects.event.EventIndexSchema; import org.opencastproject.elasticsearch.index.objects.series.Series; import org.opencastproject.elasticsearch.index.objects.series.SeriesIndexSchema; @@ -272,7 +273,7 @@ public Response getSeriesList(@HeaderParam("Accept") String acceptHeader, @Query } else if ("Creator".equals(name)) { query.withCreator(value); } else if ("textFilter".equals(name)) { - query.withText("*" + elasticsearchIndex.escapeQuery(value) + "*"); + query.withText(QueryPreprocessor.sanitize(value)); } else if ("language".equals(name)) { query.withLanguage(value); } else if ("license".equals(name)) { @@ -1341,7 +1342,7 @@ private SearchResult<Series> getSeries( q.withEdit(edit); } if (StringUtils.isNotEmpty(text)) { - q.withText(fuzzyMatch.booleanValue(), elasticsearchIndex.escapeQuery(text)); + q.withText(fuzzyMatch.booleanValue(), QueryPreprocessor.sanitize(text)); } if (StringUtils.isNotEmpty(seriesId)) { q.withIdentifier(seriesId);
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-jh6x-7xfg-9cq2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-52797ghsaADVISORY
- github.com/opencast/opencast/blob/7ad05f72814f057130122904015d471cfe5f4c58/docs/guides/admin/docs/changelog/older-versions/opencast-16.mdghsaWEB
- github.com/opencast/opencast/commit/3d5ebd163674eb18e070f52b64a18f92188f98c3ghsax_refsource_MISCWEB
- github.com/opencast/opencast/pull/5033ghsax_refsource_MISCWEB
- github.com/opencast/opencast/pull/5150ghsax_refsource_MISCWEB
- github.com/opencast/opencast/security/advisories/GHSA-jh6x-7xfg-9cq2ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.