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

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


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

commit f39012f879824ad54d0edb9776692c437aeff9fd
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, 55 insertions(+), 18 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 51d14b65993..f8a9daa2389 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -170,7 +170,8 @@ public class V2HttpCall extends HttpSolrCall {
             if (action == REMOTEPROXY) {
               action = ADMIN_OR_REMOTEPROXY;
               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;
             }
           }
@@ -188,7 +189,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 349b46a9423..cdd745ab3a0 100644
--- a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
@@ -118,8 +118,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());
     }
   }
 
@@ -133,10 +132,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 7b8d0417d20..cba47bad98d 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -154,8 +154,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());
     }
   }
 
@@ -964,7 +963,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 c2cd0148938..4102edff3e1 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
@@ -243,7 +243,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 0f0d79d36b9..edcdc0b1088 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
@@ -203,6 +203,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 45d6ea65ce4..f7598af121f 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 08321f1bf17..0b959100420 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;
@@ -228,6 +230,16 @@ public abstract class RuleBasedAuthorizationPluginBase
       return false;
     } else {
       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 97a9677b69b..108b3f637f6 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -49,6 +49,7 @@ import java.util.Random;
 import java.util.Set;
 import java.util.function.Supplier;
 import net.jcip.annotations.ThreadSafe;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.solr.api.ApiBag;
 import org.apache.solr.api.V2HttpCall;
 import org.apache.solr.client.api.util.SolrVersion;
@@ -154,7 +155,7 @@ public class HttpSolrCall {
     this.requestType = RequestType.UNKNOWN;
     this.userAgentSolrVersion = parseUserAgentSolrVersion();
     this.span = 
Optional.ofNullable(TraceUtils.getSpan(req)).orElse(Span.getInvalid());
-    this.path = ServletUtils.getPathAfterContext(req);
+    normalizeAndSetPath(ServletUtils.getPathAfterContext(req));
 
     req.setAttribute(HttpSolrCall.class.getName(), this);
     // set a request timer which can be reused by requests if needed
@@ -163,6 +164,11 @@ public class HttpSolrCall {
     req.setAttribute("org.apache.solr.CoreContainer", cores);
   }
 
+  @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;
   }
@@ -217,7 +223,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)) {
@@ -226,7 +232,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("");
@@ -255,14 +261,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);
             if (action == REMOTEPROXY) {
-              path = path.substring(idx);
+              normalizeAndSetPath(path.substring(idx));
               return;
             }
           }

Reply via email to