CVE-2015-10017: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: A vulnerability has been found in HPI-Information-Systems ProLOD and classified as critical. This vulnerability affects unknown code. The manipulation of the argument this leads to sql injection. The name of the patch is 3f710905458d49c77530bd3cbcd8960457566b73. It is recommended to apply a patch to fix this issue. The identifier of this vulnerability is VDB-217552.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
CVSS v3 base score: 9.8
Published: 2023-01-06T11:15Z
Repository: https://github.com/HPI-Information-Systems/ProLOD
Language: Scala
Files changed: prolod-common/src/main/scala/prolod/common/config/DatabaseConnection.scala, prolod-server/app/controllers/prolod/server/GraphLod.scala
Commit date: 2015-07-06 12:57:42 +0200
Commit message: validate dataset name to prevent sql injection from this parameter

Diff: diff --git a/prolod-common/src/main/scala/prolod/common/config/DatabaseConnection.scala b/prolod-common/src/main/scala/prolod/common/config/DatabaseConnection.scala
index f031bc0..06b4362 100644
--- a/prolod-common/src/main/scala/prolod/common/config/DatabaseConnection.scala
+++ b/prolod-common/src/main/scala/prolod/common/config/DatabaseConnection.scala
@@ -222,22 +222,28 @@ class DatabaseConnection(config : Configuration) {
 		var result = pattern.findFirstIn(s).gro//for (m <- pattern findFirstIn s) yield m
 		println(s + result)
 		result                */
 	}
 
-	def getClusters(s: String, ontologyNamespace : String): Seq[Group] = {
-		val table = s
+	def validateDatasetString(table: String) = {
+		if(!table.matches("[A-Za-z]+")) {
+			throw new RuntimeException("illegal table name: " + table)
+		}
+	}
+
+	def getClusters(table: String, ontologyNamespace : String): Seq[Group] = {
+		validateDatasetString(table)
 		val sql = sql"SELECT label, cluster_size FROM #${table}.CLUSTERS WHERE username = 'ontology' ORDER BY label".as[(String, Int)]
 		var id : Int = -1
 		try {
 			val result = execute(sql)
 			result map tupled((label, cluster_size) => {
 				id += 1
 				new Group(id, removeOntologyNamespace(label, ontologyNamespace), cluster_size)
 			})
 		} catch {
-			case e : SqlSyntaxErrorException => println("This dataset has no clusters: " + s)
+			case e : SqlSyntaxErrorException => println("This dataset has no clusters: " + table)
 			Nil
 		}
 	}
 
 
@@ -269,10 +275,11 @@ class DatabaseConnection(config : Configuration) {
 		result.filter(_.size > 0)
 	}
 
 
 	def getStatistics(dataset: String) : mutable.Map[String, String] = {
+		validateDatasetString(dataset)
 		val statistics = mutable.Map[String, String]()
 		val sql = sql"SELECT nodedegreedistribution, averagelinks, edges, connectedcomponents, stronglyconnectedcomponents FROM #$dataset.graphstatistics".as[(String, Float, Int, Int, Int)]
 		try {
 			val result = execute(sql) map tupled((nodedegreedistribution, averagelinks, edges, connectedcomponents, stronglyconnectedcomponents) => {
 				statistics += ("nodedegreedistribution" -> nodedegreedistribution)
@@ -295,11 +302,11 @@ class DatabaseConnection(config : Configuration) {
 			case e: SqlSyntaxErrorException => {
 				val sql2 = sql"SELECT gcnodes FROM #$dataset.graphstatistics".as[(Int)]
 				val result2 = execute(sql2) map ((gcnodes) => {
 					statistics += ("gcnodes" -> gcnodes.toString)
 				})
-				println(e.getMessage + System.lineSeparator() + sql.toString)
+				println("error getting stats: " + e.getMessage + System.lineSeparator() + sql.toString)
 			}
 		}
 		statistics
 	}
 
@@ -353,24 +360,26 @@ class DatabaseConnection(config : Configuration) {
 		}
 		patterns
 	}
 
 	def getPatternDiameter(dataset: String, patternId: Int) : Int = {
+		validateDatasetString(dataset)
 		var diameter : Int = 0
 		try {
 			val sql = sql"SELECT diameter FROM #${dataset}.patterns WHERE id = #${patternId}".as[(Int)]
 			val result = execute(sql)
 			result map ((diameterSql) => {
 				diameter = diameterSql
 			})
 		} catch {
-			case e : SqlSyntaxErrorException => println(e.getMessage)
+			case e : SqlSyntaxErrorException => println("error getting diameter" + e.getMessage)
 		}
 		diameter
 	}
 
 	def getEntityDetails(dataset: String, subjectId: Int): Entity = {
+		validateDatasetString(dataset)
 		var triples : List[Triple] = Nil
 		var label : String = ""
 		var subjectUri = ""
 		var predicateUri = ""
 		var objectUri = ""
@@ -407,11 +416,11 @@ class DatabaseConnection(config : Configuration) {
 
 				triples :::= List(new Triple(subjectUri, predicateUri, objectUri))
 			}
 			statement.close
 		} catch {
-			case e : SqlSyntaxErrorException => println(e.getMessage)
+			case e : SqlSyntaxErrorException => println("error getting entity details" + e.getMessage)
 		}
 
 		val entityDetails = new Entity(subjectId, subjectUri, label, triples)
 		entityDetails
 	}
@@ -432,10 +441,11 @@ class DatabaseConnection(config : Configuration) {
 			case e : SqlIntegrityConstraintViolationException => println("Dataset already exists")
 		}
 	}
 
 	def insertPatterns(name: String, patterns: util.HashMap[Integer, util.HashMap[String, Integer]], coloredPatterns: util.HashMap[Integer, util.List[String]], diameter: util.HashMap[Integer, lang.Double]) {
+		validateDatasetString(name)
 		val coloredPatternsMap = coloredPatterns.asScala.toMap
 		val diameterMap = diameter.asScala.toMap
 		val patternsMap = patterns.asScala.toMap
 		patternsMap.foreach {
 			case (id, patternHashMap) => {
@@ -448,21 +458,21 @@ class DatabaseConnection(config : Configuration) {
 							val patternJson : PatternFromDB = Json.parse(pattern).validate[PatternFromDB].get
 							val patternName = patternJson.name.getOrElse("")
 							val resultSet = statement.execute("INSERT INTO " + name + ".PATTERNS (ID, NAME, PATTERN, OCCURENCES, DIAMETER) VALUES (" + id + ", '" + patternName + "',  '" + pattern + "'," + occurences + "," + patternDiameter + ")")
 						} catch {
 							case e: SqlIntegrityConstraintViolationException => println("Pattern already exists")
-							case e: SqlException => println(e.getMessage)
+							case e: SqlException => println("error inserting pattern: " + e.getMessage)
 							case e: SqlSyntaxErrorException => println(e.getMessage + System.lineSeparator() + "INSERT INTO " + name + ".PATTERNS (ID, PATTERN, OCCURENCES, DIAMETER) VALUES (" + id + ", '" + pattern + "'," + occurences + ", " + patternDiameter + ")")
 						}
 						val cPattern = coloredPatternsMap.get(id).get.asScala.toList
 						cPattern.foreach { case (coloredpattern) =>
 							try {
 								val statement = connection.createStatement()
 								val resultSet = statement.execute("INSERT INTO " + name + ".coloredpatterns (ID, PATTERN) VALUES (" + id + ", '" + coloredpattern + "')")
 							} catch {
 								case e: SqlException => {
-									println(e.getMessage)
+									println("error inserting pattern (2)" + e.getMessage)
 									println(coloredpattern)
 								}
 								case e: SqlSyntaxErrorException => println(e.getMessage + System.lineSeparator() + "INSERT INTO " + name + ".coloredpatterns (ID, PATTERN) VALUES (" + id + ", '" + coloredpattern + "')")
 							}
 						}
@@ -476,10 +486,11 @@ class DatabaseConnection(config : Configuration) {
 		def hasNext: Boolean = rs.next()
 		def next(): ResultSet = rs
 	}
 
 	def performInsert(table: String, names: Seq[Any], values: Seq[Any]): Option[Int] = {
+		validateDatasetString(table)
 		val query = String.format("insert into %s (%s) values (%s)",
 			table,
 			commaize(names.map(n => n.toString).toList),
 			commaize(values.map(v => "'" + v.toString.replace("'", "") + "'").toList)
 		)
@@ -511,10 +522,11 @@ class DatabaseConnection(config : Configuration) {
 			case None => getSubjectId(name, s)
 		}
 	}
 
 	def getSubjectId(dataset: String, s: String): Int = {
+		validateDatasetString(dataset)
 		var result : Int = -1
 		val statement = connection.createStatement()
 		try {
 			val resultSet = statement.executeQuery("SELECT id FROM " + dataset + ".subjecttable WHERE subject='" + s + "'")
 			resultSet.next()
@@ -542,10 +554,11 @@ class DatabaseConnection(config : Configuration) {
 			}
 		}
 	}
 
 	def getObjectId(name: String, s: String): Int = {
+		validateDatasetString(name)
 		var result : Int = -1
 		val statement = connection.createStatement()
 		val resultSet = statement.executeQuery("SELECT tuple_id FROM " + name + ".objecttable WHERE object='" + s.replace("'", "") + "'")
 		resultSet.next()
 		result = resultSet.getInt("tuple_id")
@@ -554,32 +567,32 @@ class DatabaseConnection(config : Configuration) {
 	}
 
 	def getOntologyNamespace(s: String): String = {
 		var namespace :String = null
 		try {
-			val sql = sql"""SELECT ONTOLOGY_NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = '#${s}'""".as[String]
+			val sql = sql"""SELECT ONTOLOGY_NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = ${s}""".as[String]
 			val result = execute(sql)
 			result map ((ns) => {
 				namespace = ns
 			})
 		} catch {
-			case e: SqlException => println(e.getMessage + System.lineSeparator())
-			case e: SqlSyntaxErrorException   => println(e.getMessage + System.lineSeparator())
+			case e: SqlException => println("error getting ontology namespace: " + e.getMessage + System.lineSeparator())
+			case e: SqlSyntaxErrorException   => println("syntax in ontology namespace: " + e.getMessage + System.lineSeparator())
 		}
 		namespace
 	}
 
 	def getNamespace(s: String): String = {
 		var namespace :String = null
 		try {
-			val sql = sql"""SELECT NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = '#${s}'""".as[String]
+			val sql = sql"""SELECT NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = ${s}""".as[String]
 			val result = execute(sql)
 			result map ((ns) => {
 				namespace = ns
 			})
 		} catch {
-			case e: SqlException => println(e.getMessage + System.lineSeparator())
+			case e: SqlException => println("error getting namespace: " + e.getMessage + System.lineSeparator())
 			case e: SqlSyntaxErrorException   => println(e.getMessage + System.lineSeparator())
 		}
 		namespace
 	}
 
@@ -589,10 +602,11 @@ class DatabaseConnection(config : Configuration) {
 			case None => getPredicateId(name, s)
 		}
 	}
 
 	def getPredicateId(name: String, s: String): Int = {
+		validateDatasetString(name)
 		var result : Int = -1
 		val statement = connection.createStatement()
 		val resultSet = statement.executeQuery("SELECT id FROM " + name + ".predicatetable WHERE predicate='" + s + "'")
 		resultSet.next()
 		result = resultSet.getInt("id")
@@ -639,13 +653,14 @@ class DatabaseConnection(config : Configuration) {
 		}
 
 	}
 
 	def updateClusterSizes(dataset : String, ontologyNamespace : String) = {
+		validateDatasetString(dataset)
 		for (cluster : Group <- getClusters(dataset, ontologyNamespace)) {
 			try {
-				val sql = sql"""SELECT COUNT(*) FROM #${dataset}.MAINTABLE as m, #${dataset}.predicatetable as p, #${dataset}.objecttable as o WHERE m.predicate_id = p.id  AND o.tuple_id = m.tuple_id  AND p.predicate = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' AND o.object = '#${ontologyNamespace}#${cluster.name}'""".as[(Int)]
+				val sql = sql"""SELECT COUNT(*) FROM #${dataset}.MAINTABLE as m, #${dataset}.predicatetable as p, #${dataset}.objecttable as o WHERE m.predicate_id = p.id  AND o.tuple_id = m.tuple_id  AND p.predicate = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' AND o.object = ${ontologyNamespace + cluster.name}""".as[(Int)]
 				val result = execute(sql)
 				var clusterSize = 0
 				result map ((cluster_size) => {
 					clusterSize = cluster_size
 				})
diff --git a/prolod-server/app/controllers/prolod/server/GraphLod.scala b/prolod-server/app/controllers/prolod/server/GraphLod.scala
index 0047f95..a30998e 100644
--- a/prolod-server/app/controllers/prolod/server/GraphLod.scala
+++ b/prolod-server/app/controllers/prolod/server/GraphLod.scala
@@ -151,12 +151,14 @@ object GraphLod extends Controller {
       }
       data.nodes = entities
       data.classDistribution =  classDistribution
     }
 
-    val patternDiameter = db.getPatternDiameter(datasetId, data.patterns.last.id)
-    data.diameter = patternDiameter
+    if(data.patterns.nonEmpty) {
+      val patternDiameter = db.getPatternDiameter(datasetId, data.patterns.last.id)
+      data.diameter = patternDiameter
+    }
 
     val json = Json.obj("statistics" -> data)
     Ok(json)
   }
 

Vulnerable code: 	def getClusters(s: String, ontologyNamespace : String): Seq[Group] = {
		val table = s
			case e : SqlSyntaxErrorException => println("This dataset has no clusters: " + s)
				println(e.getMessage + System.lineSeparator() + sql.toString)
			case e : SqlSyntaxErrorException => println(e.getMessage)
			case e : SqlSyntaxErrorException => println(e.getMessage)
							case e: SqlException => println(e.getMessage)
									println(e.getMessage)
			val sql = sql"""SELECT ONTOLOGY_NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = '#${s}'""".as[String]
			case e: SqlException => println(e.getMessage + System.lineSeparator())
			case e: SqlSyntaxErrorException   => println(e.getMessage + System.lineSeparator())
			val sql = sql"""SELECT NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = '#${s}'""".as[String]
			case e: SqlException => println(e.getMessage + System.lineSeparator())
				val sql = sql"""SELECT COUNT(*) FROM #${dataset}.MAINTABLE as m, #${dataset}.predicatetable as p, #${dataset}.objecttable as o WHERE m.predicate_id = p.id  AND o.tuple_id = m.tuple_id  AND p.predicate = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' AND o.object = '#${ontologyNamespace}#${cluster.name}'""".as[(Int)]
    val patternDiameter = db.getPatternDiameter(datasetId, data.patterns.last.id)
    data.diameter = patternDiameter
Fixed code: 	def validateDatasetString(table: String) = {
		if(!table.matches("[A-Za-z]+")) {
			throw new RuntimeException("illegal table name: " + table)
		}
	}

	def getClusters(table: String, ontologyNamespace : String): Seq[Group] = {
		validateDatasetString(table)
			case e : SqlSyntaxErrorException => println("This dataset has no clusters: " + table)
		validateDatasetString(dataset)
				println("error getting stats: " + e.getMessage + System.lineSeparator() + sql.toString)
		validateDatasetString(dataset)
			case e : SqlSyntaxErrorException => println("error getting diameter" + e.getMessage)
		validateDatasetString(dataset)
			case e : SqlSyntaxErrorException => println("error getting entity details" + e.getMessage)
		validateDatasetString(name)
							case e: SqlException => println("error inserting pattern: " + e.getMessage)
									println("error inserting pattern (2)" + e.getMessage)
		validateDatasetString(table)
		validateDatasetString(dataset)
		validateDatasetString(name)
			val sql = sql"""SELECT ONTOLOGY_NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = ${s}""".as[String]
			case e: SqlException => println("error getting ontology namespace: " + e.getMessage + System.lineSeparator())
			case e: SqlSyntaxErrorException   => println("syntax in ontology namespace: " + e.getMessage + System.lineSeparator())
			val sql = sql"""SELECT NAMESPACE FROM PROLOD_MAIN.SCHEMATA WHERE SCHEMA_NAME = ${s}""".as[String]
			case e: SqlException => println("error getting namespace: " + e.getMessage + System.lineSeparator())
		validateDatasetString(name)
		validateDatasetString(dataset)
				val sql = sql"""SELECT COUNT(*) FROM #${dataset}.MAINTABLE as m, #${dataset}.predicatetable as p, #${dataset}.objecttable as o WHERE m.predicate_id = p.id  AND o.tuple_id = m.tuple_id  AND p.predicate = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' AND o.object = ${ontologyNamespace + cluster.name}""".as[(Int)]
    if(data.patterns.nonEmpty) {
      val patternDiameter = db.getPatternDiameter(datasetId, data.patterns.last.id)
      data.diameter = patternDiameter
    }