Apache Solr: Unauthorized bypass of certain "predefined permission" rules in the RuleBasedAuthorizationPlugin
Description
Deployments of Apache Solr 5.3.0 through 9.10.0 that rely on Solr's "Rule Based Authorization Plugin" are vulnerable to allowing unauthorized access to certain Solr APIs, due to insufficiently strict input validation in those components. Only deployments that meet all of the following criteria are impacted by this vulnerability:
- Use of Solr's "RuleBasedAuthorizationPlugin"
- A RuleBasedAuthorizationPlugin config (see security.json) that specifies multiple "roles"
- A RuleBasedAuthorizationPlugin permission list (see security.json) that uses one or more of the following pre-defined permission rules: "config-read", "config-edit", "schema-read", "metrics-read", or "security-read".
- A RuleBasedAuthorizationPlugin permission list that doesn't define the "all" pre-defined permission
- A networking setup that allows clients to make unfiltered network requests to Solr. (i.e. user-submitted HTTP/HTTPS requests reach Solr as-is, unmodified or restricted by any intervening proxy or gateway)
Users can mitigate this vulnerability by ensuring that their RuleBasedAuthorizationPlugin configuration specifies the "all" pre-defined permission and associates the permission with an "admin" or other privileged role. Users can also upgrade to a Solr version outside of the impacted range, such as the recently released Solr 9.10.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.solr:solr-coreMaven | >= 5.3.0, < 9.10.1 | 9.10.1 |
Affected products
1Patches
1c135e6335c71Fix 'path' and 'permission' related NPEs
10 files changed · +56 −17
solr/core/src/java/org/apache/solr/api/V2HttpCall.java+4 −2 modified@@ -169,7 +169,8 @@ public void call(SolrQueryRequest req, SolrQueryResponse rsp) { if (action == REMOTEQUERY) { action = ADMIN_OR_REMOTEQUERY; coreUrl = coreUrl.replace("/solr/", "/solr/____v2/c/"); - this.path = path = path.substring(prefix.length() + collectionName.length() + 2); + normalizeAndSetPath(path.substring(prefix.length() + collectionName.length() + 2)); + path = this.path; return; } } @@ -187,7 +188,8 @@ public void call(SolrQueryRequest req, SolrQueryResponse rsp) { } Thread.currentThread().setContextClassLoader(core.getResourceLoader().getClassLoader()); - this.path = path = path.substring(prefix.length() + pathSegments.get(1).length() + 2); + normalizeAndSetPath(path.substring(prefix.length() + pathSegments.get(1).length() + 2)); + path = this.path; // Core-level API, so populate "collection" template val parts.put(COLLECTION_PROP, origCorename); Api apiInfo = getApiInfo(core.getRequestHandlers(), path, req.getMethod(), fullPath, parts);
solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java+3 −1 modified@@ -251,7 +251,9 @@ public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) { if (action == null) return PermissionNameProvider.Name.COLL_READ_PERM; CollectionParams.CollectionAction collectionAction = CollectionParams.CollectionAction.get(action); - if (collectionAction == null) return null; + if (collectionAction == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown action: " + action); + } return collectionAction.isWrite ? PermissionNameProvider.Name.COLL_EDIT_PERM : PermissionNameProvider.Name.COLL_READ_PERM;
solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java+2 −1 modified@@ -205,6 +205,7 @@ public Name getPermissionName(AuthorizationContext ctx) { return Name.CONFIG_READ_PERM; } } - return null; + + throw new SolrException(ErrorCode.BAD_REQUEST, "Required parameter 'action' not provided"); } }
solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java+3 −1 modified@@ -190,7 +190,9 @@ public Name getPermissionName(AuthorizationContext request) { if (handler != null) { return handler.getPermissionName(request); } else { - return null; + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "Unable to identify 'info' sub-handler for path " + path); } } }
solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java+2 −1 modified@@ -43,6 +43,7 @@ import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.RequestHandlerUtils; +import org.apache.solr.handler.SchemaHandler; import org.apache.solr.handler.admin.api.GetAuthenticationConfigAPI; import org.apache.solr.handler.admin.api.GetAuthorizationConfigAPI; import org.apache.solr.handler.admin.api.ModifyNoAuthPluginSecurityConfigAPI; @@ -75,7 +76,7 @@ public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) { case "POST": return PermissionNameProvider.Name.SECURITY_EDIT_PERM; default: - return null; + throw SchemaHandler.getUnexpectedHttpMethodException(ctx.getHttpMethod()); } }
solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java+8 −1 modified@@ -40,6 +40,7 @@ import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; import org.apache.lucene.util.BytesRef; import org.apache.solr.cloud.ZkController; import org.apache.solr.common.SolrException; @@ -55,6 +56,7 @@ import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.SuppressForbidden; import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.RequestHandlerBase; @@ -105,7 +107,7 @@ public Category getCategory() { @Override public Name getPermissionName(AuthorizationContext request) { var params = request.getParams(); - String path = params.get(PATH, ""); + String path = normalizePath(params.get(PATH, "")); String detail = params.get(PARAM_DETAIL, "false"); if ("/security.json".equalsIgnoreCase(path) && "true".equalsIgnoreCase(detail)) { return Name.SECURITY_READ_PERM; @@ -425,6 +427,11 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw rsp.getValues().add(RawResponseWriter.CONTENT, printer); } + @SuppressForbidden(reason = "JDK String class doesn't offer a stripEnd equivalent") + private String normalizePath(String path) { + return StringUtils.stripEnd(path, "/"); + } + // -------------------------------------------------------------------------------------- // // --------------------------------------------------------------------------------------
solr/core/src/java/org/apache/solr/handler/SchemaHandler.java+8 −3 modified@@ -103,8 +103,7 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw handleGET(req, rsp); break; default: - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, "Unexpected HTTP method: " + httpMethod); + throw getUnexpectedHttpMethodException(httpMethod.name()); } } @@ -118,10 +117,16 @@ public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) { case "POST": return PermissionNameProvider.Name.SCHEMA_EDIT_PERM; default: - return null; + throw getUnexpectedHttpMethodException(ctx.getHttpMethod()); } } + public static SolrException getUnexpectedHttpMethodException(String methodName) + throws SolrException { + return new SolrException( + SolrException.ErrorCode.BAD_REQUEST, "Unexpected HTTP method: " + methodName); + } + private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) { try { String path = (String) req.getContext().get("path");
solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java+2 −3 modified@@ -153,8 +153,7 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw command.handleGET(); break; default: - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, "Unexpected HTTP method: " + httpMethod); + throw SchemaHandler.getUnexpectedHttpMethodException(httpMethod.name()); } } @@ -961,7 +960,7 @@ public Name getPermissionName(AuthorizationContext ctx) { case "POST": return Name.CONFIG_EDIT_PERM; default: - return null; + throw SchemaHandler.getUnexpectedHttpMethodException(ctx.getHttpMethod()); } }
solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java+12 −0 modified@@ -30,12 +30,14 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import org.apache.solr.api.AnnotatedApi; import org.apache.solr.api.Api; +import org.apache.solr.common.SolrException; import org.apache.solr.common.SpecProvider; import org.apache.solr.common.util.CommandOperation; import org.apache.solr.common.util.ValidatingJsonMap; @@ -229,6 +231,16 @@ private boolean predefinedPermissionAppliesToRequest( } else { PermissionNameProvider handler = (PermissionNameProvider) context.getHandler(); PermissionNameProvider.Name permissionName = handler.getPermissionName(context); + if (permissionName == null) { + final var errorMessage = + String.format( + Locale.ROOT, + "Unable to find 'predefined' associated with requestHandler [%s] and request [%s %s]", + handler.getClass().getName(), + context.getHttpMethod(), + context.getResource()); + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, errorMessage); + } boolean applies = permissionName != null && predefinedPermission.name.equals(permissionName.name);
solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java+12 −4 modified@@ -62,6 +62,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.jcip.annotations.ThreadSafe; +import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HeaderIterator; import org.apache.http.HttpEntity; @@ -182,6 +183,8 @@ public HttpSolrCall( this.response = response; this.retry = retry; this.requestType = RequestType.UNKNOWN; + normalizeAndSetPath(ServletUtils.getPathAfterContext(req)); + req.setAttribute(HttpSolrCall.class.getName(), this); // set a request timer which can be reused by requests if needed req.setAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE, new RTimerTree()); @@ -190,6 +193,11 @@ public HttpSolrCall( path = ServletUtils.getPathAfterContext(req); } + @SuppressForbidden(reason = "JDK String class doesn't offer a stripEnd equivalent") + protected void normalizeAndSetPath(String unnormalizedPath) { + this.path = StringUtils.stripEnd(unnormalizedPath, "/"); + } + public String getPath() { return path; } @@ -241,7 +249,7 @@ protected void init() throws Exception { // Try to resolve a Solr core name core = cores.getCore(origCorename); if (core != null) { - path = path.substring(idx); + normalizeAndSetPath(path.substring(idx)); } else { // extra mem barriers, so don't look at this before trying to get core if (cores.isCoreLoading(origCorename)) { @@ -250,7 +258,7 @@ protected void init() throws Exception { // the core may have just finished loading core = cores.getCore(origCorename); if (core != null) { - path = path.substring(idx); + normalizeAndSetPath(path.substring(idx)); } else { if (!cores.isZooKeeperAware()) { core = cores.getCore(""); @@ -279,14 +287,14 @@ protected void init() throws Exception { core = getCoreByCollection(collectionName, isPreferLeader); if (core != null) { if (idx > 0) { - path = path.substring(idx); + normalizeAndSetPath(path.substring(idx)); } } else { // if we couldn't find it locally, look on other nodes if (idx > 0) { extractRemotePath(collectionName, origCorename); if (action == REMOTEQUERY) { - path = path.substring(idx); + normalizeAndSetPath(path.substring(idx)); return; } }
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
6- github.com/advisories/GHSA-qr3p-2xj2-q7hqghsaADVISORY
- lists.apache.org/thread/d59hqbgo7p62myq7mgfpz7or8n1j7wbnghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-22022ghsaADVISORY
- www.openwall.com/lists/oss-security/2026/01/20/4ghsaWEB
- github.com/apache/solr/commit/c135e6335c7158fa26e96b0dc386f825255b47c0ghsaWEB
- issues.apache.org/jira/browse/SOLR-18054ghsaWEB
News mentions
0No linked articles in our index yet.