frankgh commented on code in PR #273:
URL: https://github.com/apache/cassandra-sidecar/pull/273#discussion_r2506238863


##########
server/src/main/java/org/apache/cassandra/sidecar/utils/FastCassandraInputValidator.java:
##########
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.utils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.cassandra.sidecar.common.server.data.Name;
+import org.apache.cassandra.sidecar.common.utils.Preconditions;
+import 
org.apache.cassandra.sidecar.config.CassandraInputValidationConfiguration;
+import 
org.apache.cassandra.sidecar.config.yaml.CassandraInputValidationConfigurationImpl;
+import org.apache.cassandra.sidecar.exceptions.CassandraInputException;
+import 
org.apache.cassandra.sidecar.exceptions.ForbiddenCassandraInputException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.VisibleForTesting;
+
+/**
+ * An implementation of the {@link CassandraInputValidator} that does not use 
regular expressions
+ * for validations and uses optimized validations.
+ */
+public class FastCassandraInputValidator extends 
RegexBasedCassandraInputValidator
+{
+    /**
+     * Longest permissible keyspace name
+     */
+    public static final int KEYSPACE_NAME_LENGTH = 48;
+    /**
+     * Longest permissible table name. See CASSANDRA-20389 (Create table fails 
on long table names) for
+     * details about the maximum length for table names.
+     */
+    public static final int TABLE_NAME_LENGTH = 222;
+    /**
+     * Default valid component name terminations
+     */
+    public static final List<String> DEFAULT_VALID_TERMINATIONS = 
List.of(".db", ".cql", ".json", ".crc32", "TOC.txt");
+
+    /**
+     * Default valid component name terminations for restricted component names
+     */
+    public static final List<String> DEFAULT_VALID_RESTRICTED_TERMINATIONS = 
List.of(".db", "TOC.txt");
+
+    @VisibleForTesting
+    final List<String> validTerminations;
+    @VisibleForTesting
+    final List<String> validRestrictedTerminations;
+
+    @VisibleForTesting
+    public FastCassandraInputValidator()
+    {
+        this(new CassandraInputValidationConfigurationImpl());
+    }
+
+    /**
+     * Constructs a new object with the provided {@code 
validationConfiguration}
+     *
+     * @param validationConfiguration a validation configuration
+     */
+    public FastCassandraInputValidator(CassandraInputValidationConfiguration 
validationConfiguration)
+    {
+        super(validationConfiguration);
+        Map<String, String> configMap = 
validationConfiguration.validatorConfiguration().namedParameters();
+        validTerminations = parseConfiguredOrDefault(configMap, 
"valid_terminations", DEFAULT_VALID_TERMINATIONS);
+        validRestrictedTerminations = parseConfiguredOrDefault(configMap, 
"valid_restricted_terminations", DEFAULT_VALID_RESTRICTED_TERMINATIONS);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Name validateKeyspaceName(@NotNull String keyspace) throws 
NullPointerException
+    {
+        Name name = new Name(keyspace);
+        validateNameLength(name, "keyspace", KEYSPACE_NAME_LENGTH);
+        validateNamePattern(name, "keyspace");
+
+        if (validationConfiguration.forbiddenKeyspaces().contains(name.name()))
+            throw new ForbiddenCassandraInputException("Forbidden keyspace: " 
+ keyspace);
+
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Name validateTableName(@NotNull String tableName)
+    {
+        Name name = new Name(tableName);
+        validateNameLength(name, "table name", TABLE_NAME_LENGTH);
+        validateNamePattern(name, "table name");
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String validateComponentName(@NotNull String componentName)
+    {
+        Objects.requireNonNull(componentName, "componentName must not be 
null");
+        Preconditions.checkArgument(!componentName.isEmpty(), () -> 
"componentName cannot be empty");
+        validateComponentName(componentName, validTerminations);
+        return componentName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String validateRestrictedComponentName(@NotNull String 
componentName)
+    {
+        Objects.requireNonNull(componentName, "componentName must not be 
null");
+        Preconditions.checkArgument(!componentName.isEmpty(), () -> 
"componentName cannot be empty");
+        validateComponentName(componentName, validRestrictedTerminations);
+        return componentName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void validateIndexName(String secondaryIndexName)
+    {
+        Preconditions.checkArgument(!secondaryIndexName.isEmpty(), 
"secondaryIndexName cannot be empty");
+        if (secondaryIndexName.charAt(0) != '.')
+            throw new CassandraInputException("Invalid secondary index name: " 
+ secondaryIndexName);
+        validateNamePattern(secondaryIndexName, secondaryIndexName, false, 
"secondary index", 1 /* skip the first character */);
+    }
+
+    /**
+     * Validates that the {@code name} is a valid name in Cassandra as defined 
by the grammar in
+     * <a 
href="https://cassandra.apache.org/doc/4.1/cassandra/cql/ddl.html#common-definitions";>Cassandra
 CQL common
+     * definitions</a>
+     *
+     * @param name          name to validate
+     * @param exceptionHint hint to add in the exception message
+     * @throws CassandraInputException when the {@code unquotedInput} has 
invalid characters
+     */
+    @Override
+    public void validateNamePattern(Name name, String exceptionHint)
+    {
+        validateNamePattern(name.name(), name.maybeQuotedName(), 
name.isSourceQuoted(), exceptionHint, 0);
+    }
+
+    /**
+     * Validates that the {@code name} is a valid name in Cassandra as defined 
by the grammar in
+     * <a 
href="https://cassandra.apache.org/doc/4.1/cassandra/cql/ddl.html#common-definitions";>Cassandra
 CQL common
+     * definitions</a>. The validation will only take into account the start 
index.
+     *
+     * @param unquotedName    the unquoted name to validate
+     * @param maybeQuotedName the name that maybe quoted
+     * @param isSourceQuoted  whether the source will be quoted
+     * @param exceptionHint   hint to add in the exception message
+     * @param startIndex      start index
+     * @throws CassandraInputException when the {@code unquotedName} has 
invalid characters
+     */
+    protected void validateNamePattern(String unquotedName, String 
maybeQuotedName, boolean isSourceQuoted,
+                                       String exceptionHint, int startIndex)
+    {
+        char c;
+        if (!isSourceQuoted)
+        {
+            // Validate the first character. Unquoted names can only begin 
with a letter
+            c = unquotedName.charAt(startIndex++);
+            if (!isLetter(c))
+                throw new CassandraInputException("Invalid characters in " + 
exceptionHint + ": " + maybeQuotedName);
+        }
+
+        while (startIndex < unquotedName.length())
+        {
+            c = unquotedName.charAt(startIndex++);
+            if (!isAlphanumeric(c) && !isUnderscore(c))
+                throw new CassandraInputException("Invalid characters in " + 
exceptionHint + ": " + maybeQuotedName);
+        }
+    }
+
+    /**
+     * Validates that the {@code name} has valid length
+     *
+     * @param name          name to validate
+     * @param exceptionHint hint to add in the exception message
+     * @param maxNameLength the maximum length for the name
+     * @throws CassandraInputException when the length of the {@code 
unquotedInput} is empty or larger than
+     *                                 {@code maxNameLength}
+     */
+    protected void validateNameLength(Name name, String exceptionHint, int 
maxNameLength)
+    {
+        String unquotedInput = name.name();
+        if (unquotedInput.isEmpty() || unquotedInput.length() > maxNameLength)
+            throw new CassandraInputException("Invalid length " + 
unquotedInput.length() +
+                                              " for " + exceptionHint + ": " + 
name.maybeQuotedName());
+    }
+
+    /**
+     * Validates that the {@code componentName} has valid characters, and it 
ends with one of the
+     * {@code validTerminations}.
+     *
+     * @param componentName     the name of the component to validate
+     * @param validTerminations a list of valid terminations for the component 
name
+     */
+    protected void validateComponentName(String componentName, List<String> 
validTerminations)
+    {
+        char c;
+        int lastIndexOfAllowedTermination = 
lastIndexOfAllowedTermination(componentName, validTerminations);
+
+        if (lastIndexOfAllowedTermination < 1)

Review Comment:
   No, that would not be possible today. I don't think it's reasonable to have 
something like that either , so we can assume it will not happen in the future



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