VYPR
High severityOSV Advisory· Published Jan 21, 2026· Updated Jan 21, 2026

Apache Solr: Unauthorized bypass of certain "predefined permission" rules in the RuleBasedAuthorizationPlugin

CVE-2026-22022

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.

PackageAffected versionsPatched versions
org.apache.solr:solr-coreMaven
>= 5.3.0, < 9.10.19.10.1

Affected products

1

Patches

1
c135e6335c71

Fix 'path' and 'permission' related NPEs

https://github.com/apache/solrJason GerlowskiJan 14, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.