github-actions[bot] commented on code in PR #69588:
URL: https://github.com/apache/doris/pull/69588#discussion_r4375462196


##########
fe/fe-connector/fe-connector-hive/src/main/java/org/apache/doris/connector/hive/HiveConnectorProvider.java:
##########
@@ -80,4 +81,11 @@ public void validatePropertiesForUpdate(
         candidate.putAll(updatedProperties);
         
HiveCatalogProperties.of(candidate).checkCreateTimeOnlyRules(updatedProperties);
     }
+
+    @Override
+    public ConnectorSecuritySurface securitySurface() {
+        // hive.metastore.uris (documented alias: uri) is the user-supplied 
thrift endpoint the FE connects
+        // to; the engine's SSRF gate rejects an intranet target there at 
CREATE / ALTER CATALOG.
+        return 
ConnectorSecuritySurface.ofSsrfEndpoints(HiveCatalogProperties.HIVE_METASTORE_URIS,
 "uri");

Review Comment:
   [P2] Include the modern HMS URI key in the declared surface
   
   With a checker configured, an existing HMS catalog can execute `ALTER 
CATALOG ... SET PROPERTIES("metastore.thrift.uris"="thrift://127.0.0.1:9083")` 
without any probe: neither declared key is touched. 
`HiveConnector.buildHmsClientConfig` preserves this property, `HmsConfHelper` 
copies it into `HiveConf`, and the actual `HiveMetaStoreClient.resolveUris` 
reads `MetastoreConf.THRIFT_URIS`. [Hive 3.1.3 gives `metastore.thrift.uris` 
precedence over 
`hive.metastore.uris`](https://github.com/apache/hive/blob/rel/release-3.1.3/standalone-metastore/src/main/java/org/apache/hadoop/hive/metastore/conf/MetastoreConf.java#L1275),
 so the next connector rebuild connects to loopback even though the stored 
legacy URI is public. CREATE with both values and `test_connection=false` 
likewise checks only the public legacy value. Include the modern key in 
endpoint selection and ALTER triggers, and cover modern-key-only updates. This 
is a missing effective input key, separate from the resolved query/frag
 ment parsing issue.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java:
##########
@@ -345,41 +352,102 @@ private void 
checkDriverUrlsAgainstOperatorGate(Map<String, String> candidate,
         }
     }
 
-    /** One row of the driver-jar key table: which properties name the jar, 
live under which flavor key. */
-    private static final class DriverUrlKeys {
-        /** The properties whose value the connector hands to a class loader, 
documented aliases included. */
-        final List<String> urlKeys;
-        /** The key whose candidate value must be "jdbc" for the urlKeys to be 
live; null = always live. */
-        final String flavorKey;
+    /**
+     * The connector's declared security-sensitive property surface, looked up 
by catalog type WITHOUT
+     * initializing a connector (so neither gate forces a catalog to come to 
life). A missing provider
+     * cannot validate newly submitted properties: accepting them would bypass 
the gates when its plugin
+     * is restored, since replay and lazy reconstruction deliberately skip 
statement-time validation.
+     * Only an available provider may declare {@link 
ConnectorSecuritySurface#EMPTY}.
+     */
+    private ConnectorSecuritySurface securitySurface(Map<String, String> 
properties) throws DdlException {
+        return ConnectorFactory.findProvider(getType(), properties)
+                .map(ConnectorProvider::securitySurface)
+                .orElseThrow(() -> new DdlException("No ConnectorProvider 
found for catalog '" + name
+                        + "', type '" + getType() + "'. Install the connector 
plugin "
+                        + "before altering catalog properties."));
+    }
 
-        DriverUrlKeys(String flavorKey, String... urlKeys) {
-            this.flavorKey = flavorKey;
-            this.urlKeys = Arrays.asList(urlKeys);
+    /**
+     * Applies the operator's SSRF policy (the third-party {@code 
SecurityChecker} SDK) to every
+     * user-supplied network endpoint these properties would make the FE 
connect to, rejecting an intranet
+     * target. It mirrors {@link #checkDriverUrlsAgainstOperatorGate}: the 
connector declares the endpoint
+     * keys through {@link ConnectorSecuritySurface#ssrfEndpointKeys()}, it 
runs on CREATE
+     * (through {@code checkWhenCreating}) and on the {@code !isReplay} ALTER 
path (here and in
+     * {@code validatePropertiesBeforeUpdate}) but never on replay, and it is 
a no-op unless the operator
+     * installed an SSRF checker.
+     *
+     * <p>The judgment itself is delegated to the SDK — see {@link 
SecurityChecker#checkEndpointForSsrf}.
+     * This is a point-in-time check of the endpoint supplied at CREATE/ALTER; 
it does not guard a later
+     * runtime connection (DNS rebinding, lazy metadata access) — that is a 
deliberate scope limit.
+     *
+     * <p>A connector whose property names a host to connect to declares it in 
its
+     * {@code securitySurface()} and nowhere else.
+     */
+    private void checkEndpointsAgainstSsrf(Map<String, String> candidate, 
Map<String, String> updatedProperties)
+            throws DdlException {
+        SecurityChecker checker = SecurityChecker.getInstance();
+        if (!checker.isEnabled()) {
+            return;
+        }
+        List<String> endpointKeys = 
securitySurface(candidate).ssrfEndpointKeys();
+        if (endpointKeys.isEmpty()) {
+            return;
+        }
+        // Only an ALTER that touches an endpoint key can repoint the catalog 
at a new host; a stored value
+        // was gated at its own CREATE/ALTER time. On CREATE the full property 
map is passed as both
+        // arguments, so every supplied endpoint counts as touched and is 
checked.
+        boolean touched = 
endpointKeys.stream().anyMatch(updatedProperties::containsKey);
+        if (!touched) {
+            return;
+        }
+        for (String key : endpointKeys) {
+            String rawValue = candidate.get(key);
+            if (rawValue == null || rawValue.trim().isEmpty()) {
+                continue;
+            }
+            // A value may list several comma-separated endpoints (e.g. HA 
hive.metastore.uris).
+            for (String endpoint : rawValue.split(",")) {
+                HostAndPort hostAndPort = parseEndpoint(endpoint.trim());

Review Comment:
   [P2] Validate the expanded HMS destination and its dependencies
   
   The client resolves Hadoop configuration variables before parsing its URI, 
while this loop parses the raw catalog text. For example, 
`hive.metastore.uris="thrift://${hms.target}:9083"`, `hms.target="127.0.0.1"`, 
and `test_connection=false` pass the holder's nonblank check; `parseEndpoint` 
rejects the unexpanded braces and this loop silently skips the probe. 
`HiveConnector` and `HmsConfHelper` preserve both properties, so [Hadoop 3.4.2 
`Configuration.get` expands the 
reference](https://github.com/apache/hadoop/blob/rel/release-3.4.2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/Configuration.java#L1263)
 before `HiveMetaStoreClient.resolveUris` connects to loopback. An ALTER 
changing only `hms.target` also repoints an existing templated catalog while 
the touched-key guard skips validation. Resolve the effective endpoint through 
the connector's configuration layer and account for dependency changes, or 
reject endpoint interpolation during interactive valida
 tion. This configuration-expansion case persists with every URI alias declared 
and is distinct from the resolved URI-suffix issue.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to