emilelm
emilelm

Reputation: 21

Register arguments for custom boolean operations in Kotlin Exposed

I'm trying out SQL server's full-text search indexes and more specifically the CONTAINS function for finding out whether a column contains an exact match for a user's full name.

I am using the Kotlin Exposed library where the contains function is not supported. So I created a custom boolean operation for this so that I could still take advantage of the Exposed DSL when writing my queries and not have to resort to native queries.

See my implementation below:

class Contains(private val columns: List<Column<*>>, private val searchText: String) : Op<Boolean>() {
    override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
        append("CONTAINS((")
        append(columns.joinToString(", ") { it.name })
        append("), ")
        registerArgument(VarCharColumnType(), "\"*$searchText*\"")
        append(")")
    }
}

internal infix fun Column<*>.contains(searchText: String): Op<Boolean> =
    Contains(columns = listOf(this), searchText = searchText)

And in my Spring repository class I have added this function for finding users by their full name:

fun findUsersByName(searchName: String): List<User> =
    Users
        .selectAll()
        .limit(100)
        .where {
            (Users.fullName contains searchName) and
            (Users.protectedIdentity eq false)
        }
        .orderBy(
            Users.firstName to SortOrder.ASC_NULLS_LAST,
            Users.lastName to SortOrder.ASC_NULLS_LAST,
            Users.userSsn to SortOrder.ASC
        )
        .map(User::wrapRow)

Now to my question, how do I ensure that my code is safe from SQL injections when defining custom boolean operations like this? How do I ensure the statement is prepared and that the user cannot inject malicious strings? I used registerArgument in my QueryBuilder but I'm not sure if it's the right way to go about it.

I wrote a test but I get a syntax error when trying to escape the string instead of the expected empty list of users which makes me suspect I did not implement the parameterization correctly.

@ParameterizedTest
@ValueSource(strings = [
    "*\"') OR 1=1;--",
    "*\"\') OR 1=1;--",
])
fun `should not be able to find all users by SQL injection string`(injectionString: String) = transaction(database) {
    assertThat(userRepository.findUsersByName(injectionString))
        .isEmpty()
}

org.jetbrains.exposed.exceptions.ExposedSQLException: com.microsoft.sqlserver.jdbc.SQLServerException: Syntax error near ''' in the full-text search condition '"**"') OR 1=1;--*"'.

I have input validation in my REST controller which ensures a user could technically not provide this string but I still want to implement it correctly just to be sure.

Upvotes: 0

Views: 92

Answers (0)

Related Questions