This is an automated email from the ASF dual-hosted git repository.

gerlowskija pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new b12604e2f80 Fix 'path' and 'permission' related NPEs
b12604e2f80 is described below

commit b12604e2f8026ba5c5759c720109b14f60a5811a
Author: Jason Gerlowski <[email protected]>
AuthorDate: Wed Jan 14 12:12:04 2026 -0500

    Fix 'path' and 'permission' related NPEs
    
    Fixes another lingering source of NPEs in v1 and v2 requests.
---
 solr/core/src/java/org/apache/solr/api/V2HttpCall.java   |  6 ++++--
 .../src/java/org/apache/solr/handler/SchemaHandler.java  | 11 ++++++++---
 .../java/org/apache/solr/handler/SolrConfigHandler.java  |  5 ++---
 .../apache/solr/handler/admin/CollectionsHandler.java    |  4 +++-
 .../org/apache/solr/handler/admin/ConfigSetsHandler.java |  3 ++-
 .../java/org/apache/solr/handler/admin/InfoHandler.java  |  4 +++-
 .../apache/solr/handler/admin/SecurityConfHandler.java   |  3 ++-
 .../apache/solr/handler/admin/ZookeeperInfoHandler.java  |  9 ++++++++-
 .../solr/security/RuleBasedAuthorizationPluginBase.java  | 12 ++++++++++++
 .../src/java/org/apache/solr/servlet/HttpSolrCall.java   | 16 ++++++++++++----
 10 files changed, 56 insertions(+), 17 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java 
b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
index 24cb696aeee..3bf13188349 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -169,7 +169,8 @@ public class V2HttpCall extends HttpSolrCall {
             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 class V2HttpCall extends HttpSolrCall {
       }
 
       
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);
diff --git a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java 
b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
index 86a5c8b2323..5988989e3dc 100644
--- a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
@@ -103,8 +103,7 @@ public class SchemaHandler extends RequestHandlerBase
         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 class SchemaHandler extends RequestHandlerBase
       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");
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java 
b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index a9cb278b2f0..e25d661faeb 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -153,8 +153,7 @@ public class SolrConfigHandler extends RequestHandlerBase
         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 class SolrConfigHandler extends RequestHandlerBase
       case "POST":
         return Name.CONFIG_EDIT_PERM;
       default:
-        return null;
+        throw 
SchemaHandler.getUnexpectedHttpMethodException(ctx.getHttpMethod());
     }
   }
 
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index f5ee17d6040..b172deeb558 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -251,7 +251,9 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
     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;
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java
index 535deb54e44..ab6ca4f6bcb 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java
@@ -205,6 +205,7 @@ public class ConfigSetsHandler extends RequestHandlerBase 
implements PermissionN
         return Name.CONFIG_READ_PERM;
       }
     }
-    return null;
+
+    throw new SolrException(ErrorCode.BAD_REQUEST, "Required parameter 
'action' not provided");
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
index 455bd361247..8b587a32fc2 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
@@ -190,7 +190,9 @@ public class InfoHandler extends RequestHandlerBase {
     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);
     }
   }
 }
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
index f5b528f3d70..0f3cc35d062 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
@@ -43,6 +43,7 @@ import org.apache.solr.common.util.Utils;
 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 abstract class SecurityConfHandler extends 
RequestHandlerBase
       case "POST":
         return PermissionNameProvider.Name.SECURITY_EDIT_PERM;
       default:
-        return null;
+        throw 
SchemaHandler.getUnexpectedHttpMethodException(ctx.getHttpMethod());
     }
   }
 
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java
index b5a8234a321..ed46a25f20f 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java
@@ -40,6 +40,7 @@ import java.util.SortedMap;
 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.cloud.ZkStateReader;
 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 final class ZookeeperInfoHandler extends 
RequestHandlerBase {
   @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 final class ZookeeperInfoHandler extends 
RequestHandlerBase {
     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, "/");
+  }
+
   // 
--------------------------------------------------------------------------------------
   //
   // 
--------------------------------------------------------------------------------------
diff --git 
a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java
 
b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java
index 7c37e034c73..e126b453f1d 100644
--- 
a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java
+++ 
b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java
@@ -30,12 +30,14 @@ import java.util.Collections;
 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 @@ public abstract class RuleBasedAuthorizationPluginBase
     } 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);
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java 
b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index 93feb4b5d15..4fab7a6af83 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -62,6 +62,7 @@ import java.util.stream.Collectors;
 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;
@@ -183,6 +184,8 @@ public class 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());
@@ -191,6 +194,11 @@ public class 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;
   }
@@ -242,7 +250,7 @@ public class HttpSolrCall {
       // 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)) {
@@ -251,7 +259,7 @@ public class HttpSolrCall {
         // 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("");
@@ -280,14 +288,14 @@ public class HttpSolrCall {
         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;
             }
           }

Reply via email to