You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by pr...@apache.org on 2026/09/30 09:24:07 UTC

(ranger) branch ranger-2.10 updated: RANGER-5815: [ranger-2.10] Implement best coding practices for validating service configs (#1271)

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

pradeepagrawal8184 pushed a commit to branch ranger-2.10
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/ranger-2.10 by this push:
     new fb8abbc7e RANGER-5815: [ranger-2.10] Implement best coding practices for validating service configs (#1271)
fb8abbc7e is described below

commit fb8abbc7e17a8fa21508337c1473bf4c36cf0c33
Author: Sanket-Shelar <12...@users.noreply.github.com>
AuthorDate: Wed Sep 30 14:54:01 2026 +0530

    RANGER-5815: [ranger-2.10] Implement best coding practices for validating service configs (#1271)
---
 .../java/org/apache/ranger/biz/ServiceMgr.java     | 149 ++++++++++++++++++++-
 1 file changed, 142 insertions(+), 7 deletions(-)

diff --git a/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
index 5f73be2bc..f406bfceb 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
@@ -20,8 +20,11 @@
 package org.apache.ranger.biz;
 
 import java.io.File;
+import java.net.InetAddress;
+import java.net.URI;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
@@ -198,11 +201,23 @@ public VXResponse validateConfig(RangerService service, ServiceStore svcStore) t
 		// check if service configs contains localhost/127.0.0.1
 		if (service != null && service.getConfigs() != null) {
 			for (Map.Entry<String, String> entry : service.getConfigs().entrySet()) {
-				if (entry.getValue() != null && StringUtils.containsIgnoreCase(entry.getValue(), "localhost")
-						|| StringUtils.containsIgnoreCase(entry.getValue(), "127.0.0.1")) {
-					URL url = getValidURL(entry.getValue());
-					if ((url != null) && (url.getHost().equalsIgnoreCase("localhost") || url.getHost().equals("127.0.0.1"))) {
-						throw new Exception("Invalid value for configuration " + entry.getKey() + ": host " + url.getHost() + " is not allowed");
+				String configValue = entry.getValue();
+				if (configValue != null && !configValue.trim().isEmpty()) {
+					String userInfo = extractUserInfo(configValue);
+					if (userInfoContainsBlockedHost(userInfo)) {
+						throw new Exception("Invalid value for configuration " + entry.getKey() + ": userinfo contains blocked host (blocked host detected)");
+					}
+
+					String host = extractHost(configValue);
+					if (host != null) {
+						if (isBlockedHost(host)) {
+							throw new Exception("Invalid value for configuration " + entry.getKey() + ": host " + host + " is not allowed (blocked host detected)");
+						}
+					} else {
+						String lowerVal = configValue.toLowerCase().trim();
+						if (lowerVal.contains("localhost") || lowerVal.contains("127.0.0.1") || lowerVal.contains("0.0.0.0") || lowerVal.contains("::1")) {
+							throw new Exception("Invalid value for configuration " + entry.getKey() + ": contains blocked keywords but could not be parsed securely.");
+						}
 					}
 				}
 			}
@@ -239,9 +254,129 @@ public VXResponse validateConfig(RangerService service, ServiceStore svcStore) t
 		return ret;
 	}
 
-	private static URL getValidURL(String urlString) {
+	private static URI toConfigUri(String urlString) throws Exception {
+		String processedUrl = urlString.trim().replaceFirst("(?i)^jdbc:", "");
+		if (!processedUrl.contains("://")) {
+			processedUrl = "http://" + processedUrl;
+		}
+
+		// Bracket IPv6 literals only (must contain ':' in the host segment; avoids decimal IPs like 2130706433:6379)
+		processedUrl = processedUrl.replaceFirst("://((?:[0-9a-fA-F]*:)+[0-9a-fA-F]*):(\\d+)(/.*)?$", "://[$1]:$2$3");
+
+		return new URI(processedUrl);
+	}
+
+	private static String extractUserInfo(String urlString) {
 		try {
-			return new URL(urlString);
+			return toConfigUri(urlString).getUserInfo();
+		} catch (Exception e) {
+			LOG.debug("Failed to extract userinfo from string: {}", urlString, e);
+			return null;
+		}
+	}
+
+	private static boolean userInfoContainsBlockedHost(String userInfo) {
+		if (StringUtils.isBlank(userInfo)) {
+			return false;
+		}
+
+		String[] segments = userInfo.split(":");
+		for (String segment : segments) {
+			if (StringUtils.isNotBlank(segment) && isBlockedHost(segment.trim())) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	private static String extractHost(String urlString) {
+		try {
+			URI    uri  = toConfigUri(urlString);
+			String host = uri.getHost();
+
+			if (host == null && uri.getAuthority() != null) {
+				host = uri.getAuthority();
+				// Strip credentials if present (e.g., user:pass@127.1)
+				if (host.contains("@")) {
+					host = host.substring(host.lastIndexOf("@") + 1);
+				}
+
+				if (host.startsWith("[")) {
+					int closeBracket = host.indexOf("]");
+					int portColon    = host.indexOf(":", closeBracket);
+					if (portColon > -1) {
+						host = host.substring(0, portColon);
+					}
+				} else if (host.contains(":")) {
+					host = host.substring(0, host.indexOf(":"));
+				}
+			}
+
+			return host != null ? host.replaceAll("^\\[|\\]$", "") : null;
+		} catch (Exception e) {
+			LOG.debug("Failed to extract host from string: {}", urlString, e);
+			return null;
+		}
+	}
+
+	private static boolean isBlockedHost(String host) {
+		if (host == null || host.trim().isEmpty()) {
+			return false;
+		}
+		host = host.trim().toLowerCase();
+
+		// Expand POSIX shorthand IPs (e.g., 127.1 -> 127.0.0.1)
+		String normalizedIP = normalizePOSIXIp(host);
+		if (normalizedIP != null) {
+			host = normalizedIP;
+		}
+
+		try {
+			InetAddress addr = InetAddress.getByName(host);
+
+			return addr.isLoopbackAddress() || addr.isLinkLocalAddress() || addr.isAnyLocalAddress() || "localhost".equals(host);
+		} catch (UnknownHostException e) {
+			LOG.debug("Host could not be resolved, allowing by default: {}", host);
+			return false;
+		}
+	}
+
+	private static String normalizePOSIXIp(String host) {
+		String[] parts = host.split("\\.");
+		if (parts.length < 1 || parts.length > 4) {
+			return null;
+		}
+
+		try {
+			long[] vals = new long[parts.length];
+			for (int i = 0; i < parts.length; i++) {
+				String p = parts[i].trim();
+				if (p.isEmpty()) {
+					return null;
+				}
+
+				int radix = (p.startsWith("0x") || p.startsWith("0X")) ? 16 : (p.startsWith("0") && p.length() > 1 ? 8 : 10);
+				vals[i] = Long.parseLong(p.replaceFirst("(?i)^0x", ""), radix);
+			}
+
+			long ip = 0;
+			switch (parts.length) {
+				case 1:
+					ip = vals[0];
+					break;
+				case 2:
+					ip = (vals[0] << 24) | vals[1];
+					break;
+				case 3:
+					ip = (vals[0] << 24) | (vals[1] << 16) | vals[2];
+					break;
+				case 4:
+					ip = (vals[0] << 24) | (vals[1] << 16) | (vals[2] << 8) | vals[3];
+					break;
+			}
+
+			return String.format("%d.%d.%d.%d", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
 		} catch (Exception e) {
 			return null;
 		}