import slick.jdbc.H2Profile.api._
import scala.concurrent.Future
class UserRepository(db: Database) {
// Vulnerable: Direct string interpolation in SQL
def findUserByName(name: String): Future[Seq[User]] = {
val query = sql"SELECT * FROM users WHERE name = '$name'".as[User]
db.run(query)
}
// Vulnerable: sqlu with user input
def updateUserStatus(userId: Int, status: String): Future[Int] = {
val updateQuery = sqlu"UPDATE users SET status = '$status' WHERE id = $userId"
db.run(updateQuery)
}
// Vulnerable: Complex query building
def searchUsers(criteria: Map[String, String]): Future[Seq[User]] = {
val conditions = criteria.map { case (field, value) =>
s"$field LIKE '%$value%'"
}.mkString(" AND ")
val query = sql"SELECT * FROM users WHERE #$conditions".as[User]
db.run(query)
}
// Extremely vulnerable: Direct SQL execution
def executeRawQuery(sqlQuery: String): Future[Vector[String]] = {
val query = sql"#$sqlQuery".as[String]
db.run(query)
}
}
import slick.jdbc.H2Profile.api._
import scala.concurrent.Future
import scala.util.{Try, Success, Failure}
case class User(id: Int, name: String, email: String, status: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
def status = column[String]("status")
def * = (id, name, email, status) <> (User.tupled, User.unapply)
}
class UserRepository(db: Database) {
val users = TableQuery[Users]
// Input validation helper
def validateString(input: String, maxLength: Int = 100): Either[String, String] = {
if (input == null || input.trim.isEmpty) {
Left("Input cannot be empty")
} else if (input.length > maxLength) {
Left(s"Input too long (max $maxLength characters)")
} else if (input.contains("'") || input.contains("\"") || input.contains(";")) {
Left("Input contains invalid characters")
} else {
Right(input.trim)
}
}
def validateUserId(id: String): Either[String, Int] = {
Try(id.toInt) match {
case Success(value) if value > 0 => Right(value)
case _ => Left("Invalid user ID")
}
}
// Secure: Use type-safe Slick queries
def findUserByName(name: String): Future[Either[String, Seq[User]]] = {
validateString(name, 50) match {
case Right(validName) =>
val query = users.filter(_.name === validName)
db.run(query.result).map(Right(_))
case Left(error) =>
Future.successful(Left(error))
}
}
// Secure: Use parameterized queries
def findUserByNamePattern(namePattern: String): Future[Either[String, Seq[User]]] = {
validateString(namePattern, 30) match {
case Right(validPattern) =>
// Safe: Use # for parameter binding
val query = sql"SELECT id, name, email, status FROM users WHERE name LIKE ${s"%$validPattern%"}".as[User]
db.run(query).map(Right(_))
case Left(error) =>
Future.successful(Left(error))
}
}
// Secure: Type-safe update
def updateUserStatus(userIdStr: String, status: String): Future[Either[String, Int]] = {
val validStatuses = Set("active", "inactive", "pending", "suspended")
for {
userId <- Future.fromTry(Try(validateUserId(userIdStr)).flatten.toTry)
validStatus <- Future.fromTry {
if (validStatuses.contains(status)) Try(status)
else Try(throw new IllegalArgumentException("Invalid status"))
}
} yield {
val updateQuery = users.filter(_.id === userId).map(_.status).update(validStatus)
db.run(updateQuery).map(Right(_))
}
}.flatten.recover {
case ex: IllegalArgumentException => Left(ex.getMessage)
case _ => Left("Update failed")
}
// Secure: Safe search with type-safe queries
def searchUsers(criteria: Map[String, String]): Future[Either[String, Seq[User]]] = {
// Validate all criteria
val validatedCriteria = criteria.map { case (field, value) =>
val allowedFields = Set("name", "email", "status")
if (!allowedFields.contains(field)) {
return Future.successful(Left(s"Field '$field' not allowed"))
}
validateString(value, 50) match {
case Right(validValue) => (field, validValue)
case Left(error) => return Future.successful(Left(s"Invalid value for $field: $error"))
}
}
// Build type-safe query
var query = users.filter(_ => true) // Start with all users
validatedCriteria.foreach { case (field, value) =>
field match {
case "name" => query = query.filter(_.name like s"%$value%")
case "email" => query = query.filter(_.email like s"%$value%")
case "status" => query = query.filter(_.status === value)
}
}
db.run(query.result).map(Right(_))
}
// Secure: No raw query execution
def executeRawQuery(sqlQuery: String): Future[Either[String, String]] = {
Future.successful(Left("Raw query execution is disabled for security"))
}
// Alternative: Safe complex queries using Slick API
def findUsersWithComplexCriteria(
nameOpt: Option[String],
statusOpt: Option[String],
limit: Int = 100
): Future[Either[String, Seq[User]]] = {
// Validate inputs
val validLimit = math.min(limit, 1000) // Cap at 1000
var query = users
nameOpt.foreach { name =>
validateString(name, 50) match {
case Right(validName) => query = query.filter(_.name like s"%$validName%")
case Left(error) => return Future.successful(Left(error))
}
}
statusOpt.foreach { status =>
val allowedStatuses = Set("active", "inactive", "pending", "suspended")
if (allowedStatuses.contains(status)) {
query = query.filter(_.status === status)
} else {
return Future.successful(Left("Invalid status"))
}
}
val finalQuery = query.take(validLimit)
db.run(finalQuery.result).map(Right(_))
}
// Secure: Use prepared statements for dynamic queries
def dynamicSearch(searchTerms: List[String]): Future[Either[String, Seq[User]]] = {
if (searchTerms.isEmpty) {
return Future.successful(Right(Seq.empty))
}
// Validate all search terms
val validatedTerms = searchTerms.take(5).map { term =>
validateString(term, 30) match {
case Right(validTerm) => validTerm
case Left(error) => return Future.successful(Left(error))
}
}
// Build safe query with parameters
val conditions = validatedTerms.map { term =>
users.filter(u => u.name.like(s"%$term%") || u.email.like(s"%$term%"))
}
// Union all conditions
val unionQuery = conditions.reduce(_ union _)
db.run(unionQuery.result).map(Right(_))
}
}