[db] escape search query

to prevent sqlite FTS search features from messing up query

Fixes acra-crash-reports#217
This commit is contained in:
Torsten Grote 2023-03-07 13:36:07 -03:00 committed by Hans-Christoph Steiner
parent 8d61de333b
commit 21f40158a2
2 changed files with 42 additions and 4 deletions

View File

@ -62,6 +62,12 @@ internal class AppListItemsTest : AppTest() {
assertEquals(app1, apps[0])
}
// get first app by partial search, sort by name
appDao.getAppListItems(pm, "On", NAME).getOrFail().let { apps ->
assertEquals(1, apps.size)
assertEquals(app1, apps[0])
}
// get second app by search, sort order doesn't matter
appDao.getAppListItems(pm, "Two", NAME).getOrFail().let { apps ->
assertEquals(1, apps.size)
@ -155,6 +161,27 @@ internal class AppListItemsTest : AppTest() {
}
}
@Test
fun testMalformedSearchQuery() {
every { pm.getInstalledPackages(0) } returns emptyList()
// without category
appDao.getAppListItems(pm, "\"", LAST_UPDATED).getOrFail().let { apps ->
assertTrue(apps.isEmpty())
}
appDao.getAppListItems(pm, "*simple\"*", NAME).getOrFail().let { apps ->
assertTrue(apps.isEmpty())
}
// with category
appDao.getAppListItems(pm, "Category", "\"", LAST_UPDATED).getOrFail().let { apps ->
assertTrue(apps.isEmpty())
}
appDao.getAppListItems(pm, "Category", "*simple\"*", NAME).getOrFail().let { apps ->
assertTrue(apps.isEmpty())
}
}
@Test
fun testSortOrderByLastUpdated() {
// insert three apps in a random order

View File

@ -379,7 +379,7 @@ internal interface AppDaoInt : AppDao {
return if (searchQuery.isNullOrEmpty()) when (sortOrder) {
LAST_UPDATED -> getAppListItemsByLastUpdated().map(packageManager)
NAME -> getAppListItemsByName().map(packageManager)
} else getAppListItems(searchQuery).map(packageManager)
} else getAppListItems(escapeQuery(searchQuery)).map(packageManager)
}
override fun getAppListItems(
@ -391,7 +391,12 @@ internal interface AppDaoInt : AppDao {
return if (searchQuery.isNullOrEmpty()) when (sortOrder) {
LAST_UPDATED -> getAppListItemsByLastUpdated(category).map(packageManager)
NAME -> getAppListItemsByName(category).map(packageManager)
} else getAppListItems(category, searchQuery).map(packageManager)
} else getAppListItems(category, escapeQuery(searchQuery)).map(packageManager)
}
private fun escapeQuery(searchQuery: String): String {
val sanitized = searchQuery.replace(Regex.fromLiteral("\""), "\"\"")
return "\"*$sanitized*\""
}
private fun LiveData<List<AppListItem>>.map(
@ -408,6 +413,9 @@ internal interface AppDaoInt : AppDao {
}
}
/**
* Warning: Run [escapeQuery] on the given [searchQuery] before.
*/
@Transaction
@Query("""
SELECT repoId, packageName, app.localizedName, app.localizedSummary, app.lastUpdated,
@ -416,10 +424,13 @@ internal interface AppDaoInt : AppDao {
JOIN ${AppMetadataFts.TABLE} USING (repoId, packageName)
LEFT JOIN ${HighestVersion.TABLE} AS version USING (repoId, packageName)
JOIN ${RepositoryPreferences.TABLE} AS pref USING (repoId)
WHERE pref.enabled = 1 AND ${AppMetadataFts.TABLE} MATCH '"*' || :searchQuery || '*"'
WHERE pref.enabled = 1 AND ${AppMetadataFts.TABLE} MATCH :searchQuery
GROUP BY packageName HAVING MAX(pref.weight)""")
fun getAppListItems(searchQuery: String): LiveData<List<AppListItem>>
/**
* Warning: Run [escapeQuery] on the given [searchQuery] before.
*/
@Transaction
@Query("""
SELECT repoId, packageName, app.localizedName, app.localizedSummary, app.lastUpdated,
@ -429,7 +440,7 @@ internal interface AppDaoInt : AppDao {
LEFT JOIN ${HighestVersion.TABLE} AS version USING (repoId, packageName)
JOIN ${RepositoryPreferences.TABLE} AS pref USING (repoId)
WHERE pref.enabled = 1 AND categories LIKE '%,' || :category || ',%' AND
${AppMetadataFts.TABLE} MATCH '"*' || :searchQuery || '*"'
${AppMetadataFts.TABLE} MATCH :searchQuery
GROUP BY packageName HAVING MAX(pref.weight)""")
fun getAppListItems(category: String, searchQuery: String): LiveData<List<AppListItem>>