yuqi1129 commented on code in PR #11354:
URL: https://github.com/apache/gravitino/pull/11354#discussion_r3354196409


##########
core/src/main/java/org/apache/gravitino/job/JobManager.java:
##########
@@ -830,6 +832,57 @@ static String fetchFileFromUri(String uri, File 
stagingDir, int timeoutInMs) {
     }
   }
 
+  /**
+   * Resolves the host in the given URI and rejects addresses that should not 
be reachable from the
+   * server (loopback, link-local, RFC-1918 private ranges, IPv6 ULA, cloud 
metadata endpoints).
+   * This is a defence-in-depth measure against Server-Side Request Forgery 
(SSRF).
+   *
+   * <p><b>Note on DNS TOCTOU:</b> This method resolves the hostname once at 
validation time. {@code
+   * FileUtils.copyURLToFile()} will re-resolve it when opening the 
connection, so a precisely-timed
+   * DNS-rebinding attack could theoretically bypass this check. Complete 
protection against DNS
+   * rebinding requires network-level egress controls (e.g. firewall rules) in 
addition to this
+   * application-layer validation.
+   */
+  @VisibleForTesting
+  static void validateRemoteUri(URI uri) throws IOException {
+    String host = uri.getHost();
+    if (host == null) {
+      throw new IllegalArgumentException("URI has no host: " + uri);
+    }
+    InetAddress[] addresses = InetAddress.getAllByName(host);
+    for (InetAddress address : addresses) {
+      if (isBlockedAddress(address)) {
+        throw new IllegalArgumentException(
+            String.format(
+                "URI '%s' resolves to blocked address %s, access denied (SSRF 
prevention)",
+                uri, address.getHostAddress()));
+      }
+    }
+  }
+
+  private static boolean isBlockedAddress(InetAddress address) {
+    // Covers loopback (127.x.x.x / ::1), link-local (169.254.x.x / fe80::/10 
— includes AWS/GCP/
+    // Azure metadata), RFC-1918 private (10.x / 172.16-31.x / 192.168.x), 
multicast, unspecified.
+    if (address.isLoopbackAddress()
+        || address.isLinkLocalAddress()
+        || address.isSiteLocalAddress()
+        || address.isMulticastAddress()
+        || address.isAnyLocalAddress()) {
+      return true;

Review Comment:
   The main risk is SSRF. `localhost` or a private IP is resolved from the 
Gravitino server side, not from the user's machine.
   
   So if a user passes `http://127.0.0.1:xxx` or `http://192.168.x.x`, 
Gravitino will try to access services on the Gravitino server itself or inside 
the server's internal network. This may expose local admin services, internal 
APIs, or cloud metadata endpoints.
   
   I agree that some deployments may want to fetch files from a local HTTP/FTP 
server. But I think this should be disabled by default, and only enabled 
explicitly by an admin, for example with an allowlist/configuration. If we 
reach an agreement on this point, I can do as it is. 
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to