From 4b3d30897a25ac55bea0edc88ded91675264bd9d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 5 Aug 2026 00:15:48 +0100 Subject: [PATCH] orientdb: validate class name --- .../orientdb/impl/OrientDbSourceStage.scala | 15 ++++ .../impl/OrientDbSourceStageSpec.scala | 77 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 orientdb/src/test/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStageSpec.scala diff --git a/orientdb/src/main/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStage.scala b/orientdb/src/main/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStage.scala index db7b85eec..c3b54ef4a 100644 --- a/orientdb/src/main/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStage.scala +++ b/orientdb/src/main/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStage.scala @@ -30,6 +30,19 @@ import scala.jdk.OptionConverters._ /** * INTERNAL API */ +@InternalApi +private[orientdb] object OrientDbSourceStage { + // Valid OrientDB class names: letters, digits, underscores; must start with letter or underscore + private val ValidClassName = "^[a-zA-Z_][a-zA-Z0-9_]*$".r + + private[orientdb] def validateClassName(name: String): Unit = { + require(name != null && name.nonEmpty, "className must not be null or empty") + require( + ValidClassName.matches(name), + s"className contains invalid characters: '$name'. Only letters, digits, and underscores are allowed.") + } +} + @InternalApi private[orientdb] final class OrientDbSourceStage[T](className: String, query: Option[String], @@ -37,6 +50,8 @@ private[orientdb] final class OrientDbSourceStage[T](className: String, clazz: Option[Class[T]] = None) extends GraphStage[SourceShape[OrientDbReadResult[T]]] { + OrientDbSourceStage.validateClassName(className) + val out: Outlet[OrientDbReadResult[T]] = Outlet("OrientDBSource.out") override val shape = SourceShape(out) override def initialAttributes: Attributes = diff --git a/orientdb/src/test/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStageSpec.scala b/orientdb/src/test/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStageSpec.scala new file mode 100644 index 000000000..0ef5c4993 --- /dev/null +++ b/orientdb/src/test/scala/org/apache/pekko/stream/connectors/orientdb/impl/OrientDbSourceStageSpec.scala @@ -0,0 +1,77 @@ +/* + * 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.pekko.stream.connectors.orientdb.impl + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +class OrientDbSourceStageSpec extends AnyWordSpec with Matchers { + + "OrientDbSourceStage.validateClassName" should { + + "accept valid class names" in { + OrientDbSourceStage.validateClassName("User") + OrientDbSourceStage.validateClassName("MyClass") + OrientDbSourceStage.validateClassName("_internal") + OrientDbSourceStage.validateClassName("Class123") + OrientDbSourceStage.validateClassName("my_class") + } + + "reject null class name" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName(null) + } + } + + "reject empty class name" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName("") + } + } + + "reject class name with SQL injection attempt" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName("User; DROP TABLE users; --") + } + } + + "reject class name with quotes" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName("User\" OR 1=1 --") + } + } + + "reject class name starting with digit" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName("123Class") + } + } + + "reject class name with spaces" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName("My Class") + } + } + + "reject class name with special characters" in { + assertThrows[IllegalArgumentException] { + OrientDbSourceStage.validateClassName("Class@name") + } + } + } +}