summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-08-29 03:07:08 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-08-29 03:07:08 +0000
commit6eec36d04e89422bb0c144803fc48786b00454b9 (patch)
treeab84a860015cce488ab8fcd466729beed1960911
parent4e8d5d3643983739cc6a3a3b8c186817ce5f17b5 (diff)
parent6ec80b9dfcdf9c68ed157192419ebe9002514a45 (diff)
downloadTvProvider-android-10.0.0_r15.tar.gz
Change-Id: I79a58b57a137ce5e5a0b970aad7dc158431652a6
-rw-r--r--src/com/android/providers/tv/TvProvider.java14
-rw-r--r--src/com/android/providers/tv/util/SqliteTokenFinder.java181
-rw-r--r--tests/src/com/android/providers/tv/util/SqliteTokenFinderTest.java275
3 files changed, 470 insertions, 0 deletions
diff --git a/src/com/android/providers/tv/TvProvider.java b/src/com/android/providers/tv/TvProvider.java
index d57eb8b..78dd724 100644
--- a/src/com/android/providers/tv/TvProvider.java
+++ b/src/com/android/providers/tv/TvProvider.java
@@ -63,6 +63,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.providers.tv.util.SqlParams;
+import com.android.providers.tv.util.SqliteTokenFinder;
+import java.util.Locale;
import libcore.io.IoUtils;
import java.io.ByteArrayOutputStream;
@@ -1699,6 +1701,18 @@ public class TvProvider extends ContentProvider {
private SqlParams createSqlParams(String operation, Uri uri, String selection,
String[] selectionArgs) {
int match = sUriMatcher.match(uri);
+
+ SqliteTokenFinder.findTokens(selection, p -> {
+ if (p.first == SqliteTokenFinder.TYPE_REGULAR
+ && TextUtils.equals(p.second.toUpperCase(Locale.US), "SELECT")) {
+ // only when a keyword is not in quotes or brackets
+ // see https://www.sqlite.org/lang_keywords.html
+ android.util.EventLog.writeEvent(0x534e4554, "135269669", -1, "");
+ throw new SecurityException(
+ "Subquery is not allowed in selection: " + selection);
+ }
+ });
+
SqlParams params = new SqlParams(null, selection, selectionArgs);
// Control access to EPG data (excluding watched programs) when the caller doesn't have all
diff --git a/src/com/android/providers/tv/util/SqliteTokenFinder.java b/src/com/android/providers/tv/util/SqliteTokenFinder.java
new file mode 100644
index 0000000..bb0bac2
--- /dev/null
+++ b/src/com/android/providers/tv/util/SqliteTokenFinder.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed 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 com.android.providers.tv.util;
+
+
+import android.annotation.Nullable;
+
+import android.util.Pair;
+import java.util.function.Consumer;
+
+/**
+ * Simple SQL parser to check statements for usage of prohibited/sensitive fields. Modified from
+ * packages/providers/ContactsProvider/src/com/android/providers/contacts/sqlite/SqlChecker.java
+ */
+public class SqliteTokenFinder {
+ public static final int TYPE_REGULAR = 0;
+ public static final int TYPE_IN_SINGLE_QUOTES = 1;
+ public static final int TYPE_IN_DOUBLE_QUOTES = 2;
+ public static final int TYPE_IN_BACKQUOTES = 3;
+ public static final int TYPE_IN_BRACKETS = 4;
+
+ private static boolean isAlpha(char ch) {
+ return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || (ch == '_');
+ }
+
+ private static boolean isNum(char ch) {
+ return ('0' <= ch && ch <= '9');
+ }
+
+ private static boolean isAlNum(char ch) {
+ return isAlpha(ch) || isNum(ch);
+ }
+
+ private static boolean isAnyOf(char ch, String set) {
+ return set.indexOf(ch) >= 0;
+ }
+
+ private static char peek(String s, int index) {
+ return index < s.length() ? s.charAt(index) : '\0';
+ }
+
+ /**
+ * SQL Tokenizer specialized to extract tokens from SQL (snippets).
+ *
+ * Based on sqlite3GetToken() in tokenzie.c in SQLite.
+ *
+ * Source for v3.8.6 (which android uses): http://www.sqlite.org/src/artifact/ae45399d6252b4d7
+ * (Latest source as of now: http://www.sqlite.org/src/artifact/78c8085bc7af1922)
+ *
+ * Also draft spec: http://www.sqlite.org/draft/tokenreq.html
+ *
+ * @param sql the SQL clause to be tokenized.
+ * @param checker the {@link Consumer} to check each token. The input of the checker is a pair
+ * of token type and the token.
+ */
+ public static void findTokens(@Nullable String sql, Consumer<Pair<Integer, String>> checker) {
+ if (sql == null) {
+ return;
+ }
+ int pos = 0;
+ final int len = sql.length();
+ while (pos < len) {
+ final char ch = peek(sql, pos);
+
+ // Regular token.
+ if (isAlpha(ch)) {
+ final int start = pos;
+ pos++;
+ while (isAlNum(peek(sql, pos))) {
+ pos++;
+ }
+ final int end = pos;
+
+ final String token = sql.substring(start, end);
+ checker.accept(Pair.create(TYPE_REGULAR, token));
+
+ continue;
+ }
+
+ // Handle quoted tokens
+ if (isAnyOf(ch, "'\"`")) {
+ final int quoteStart = pos;
+ pos++;
+
+ for (;;) {
+ pos = sql.indexOf(ch, pos);
+ if (pos < 0) {
+ throw new IllegalArgumentException("Unterminated quote in" + sql);
+ }
+ if (peek(sql, pos + 1) != ch) {
+ break;
+ }
+ // Quoted quote char -- e.g. "abc""def" is a single string.
+ pos += 2;
+ }
+ final int quoteEnd = pos;
+ pos++;
+
+ // Extract the token
+ String token = sql.substring(quoteStart + 1, quoteEnd);
+ // Unquote if needed. i.e. "aa""bb" -> aa"bb
+ if (token.indexOf(ch) >= 0) {
+ token = token.replaceAll(String.valueOf(ch) + ch, String.valueOf(ch));
+ }
+ int type = TYPE_REGULAR;
+ switch (ch) {
+ case '\'':
+ type = TYPE_IN_SINGLE_QUOTES;
+ break;
+ case '\"':
+ type = TYPE_IN_DOUBLE_QUOTES;
+ break;
+ case '`':
+ type = TYPE_IN_BACKQUOTES;
+ break;
+ }
+ checker.accept(Pair.create(type, token));
+ continue;
+ }
+ // Handle tokens enclosed in [...]
+ if (ch == '[') {
+ final int quoteStart = pos;
+ pos++;
+
+ pos = sql.indexOf(']', pos);
+ if (pos < 0) {
+ throw new IllegalArgumentException("Unterminated quote in" + sql);
+ }
+ final int quoteEnd = pos;
+ pos++;
+
+ final String token = sql.substring(quoteStart + 1, quoteEnd);
+
+ checker.accept(Pair.create(TYPE_IN_BRACKETS, token));
+ continue;
+ }
+
+ // Detect comments.
+ if (ch == '-' && peek(sql, pos + 1) == '-') {
+ pos += 2;
+ pos = sql.indexOf('\n', pos);
+ if (pos < 0) {
+ // strings ending in an inline comment.
+ break;
+ }
+ pos++;
+
+ continue;
+ }
+ if (ch == '/' && peek(sql, pos + 1) == '*') {
+ pos += 2;
+ pos = sql.indexOf("*/", pos);
+ if (pos < 0) {
+ throw new IllegalArgumentException("Unterminated comment in" + sql);
+ }
+ pos += 2;
+
+ continue;
+ }
+
+ // For this purpose, we can simply ignore other characters.
+ // (Note it doesn't handle the X'' literal properly and reports this X as a token,
+ // but that should be fine...)
+ pos++;
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/tv/util/SqliteTokenFinderTest.java b/tests/src/com/android/providers/tv/util/SqliteTokenFinderTest.java
new file mode 100644
index 0000000..f228080
--- /dev/null
+++ b/tests/src/com/android/providers/tv/util/SqliteTokenFinderTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed 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 com.android.providers.tv.util;
+
+import android.annotation.Nullable;
+import android.test.AndroidTestCase;
+import android.util.Pair;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link SqliteTokenFinder}.
+ *
+ * Modified from
+ * packages/providers/ContactsProvider/tests/src/com/android/providers/contacts/sqlite/SqlCheckerTest.java
+ */
+public class SqliteTokenFinderTest extends AndroidTestCase {
+ private List<Pair<Integer, String>> getTokens(String sql) {
+ List<Pair<Integer, String>> tokens = new ArrayList<>();
+ SqliteTokenFinder.findTokens(sql, new Consumer<Pair<Integer, String>>() {
+ @Override
+ public void accept(Pair<Integer, String> pair) {
+ tokens.add(pair);
+ }
+ });
+ return tokens;
+ }
+
+ private void checkTokens(String sql, Pair<Integer, String>... tokens) {
+ final List<Pair<Integer, String>> expected = Arrays.asList(tokens);
+
+ assertEquals(expected, getTokens(sql));
+ }
+
+ private void checkTokensRegular(String sql, @Nullable String tokens) {
+ List<Pair<Integer, String>> expected = new ArrayList<>();
+
+ if (tokens != null) {
+ for (String token : tokens.split(" ")) {
+ expected.add(Pair.create(SqliteTokenFinder.TYPE_REGULAR, token));
+ }
+ }
+
+ assertEquals(expected, getTokens(sql));
+ }
+
+ private void assertInvalidSql(String sql, String message) {
+ try {
+ getTokens(sql);
+ fail("Didn't throw Exception");
+ } catch (Exception e) {
+ assertTrue("Expected " + e.getMessage() + " to contain " + message,
+ e.getMessage().contains(message));
+ }
+ }
+
+ public void testWhitespaces() {
+ checkTokensRegular(" select \t\r\n a\n\n ", "select a");
+ checkTokensRegular("a b", "a b");
+ }
+
+ public void testComment() {
+ checkTokensRegular("--\n", null);
+ checkTokensRegular("a--\n", "a");
+ checkTokensRegular("a--abcdef\n", "a");
+ checkTokensRegular("a--abcdef\nx", "a x");
+ checkTokensRegular("a--\nx", "a x");
+ checkTokensRegular("a--abcdef", "a");
+ checkTokensRegular("a--abcdef\ndef--", "a def");
+
+ checkTokensRegular("/**/", null);
+ assertInvalidSql("/*", "Unterminated comment");
+ assertInvalidSql("/*/", "Unterminated comment");
+ assertInvalidSql("/*\n* /*a", "Unterminated comment");
+ checkTokensRegular("a/**/", "a");
+ checkTokensRegular("/**/b", "b");
+ checkTokensRegular("a/**/b", "a b");
+ checkTokensRegular("a/* -- \n* /* **/b", "a b");
+ }
+
+ public void testSingleQuotes() {
+ assertInvalidSql("'", "Unterminated quote");
+ assertInvalidSql("a'", "Unterminated quote");
+ assertInvalidSql("a'''", "Unterminated quote");
+ assertInvalidSql("a''' ", "Unterminated quote");
+ checkTokens("''", Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, ""));
+
+ // 2 consecutive quotes inside quotes stands for a quote. e.g.'let''s go' -> let's go
+ checkTokens(
+ "''''",
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "\'"));
+ checkTokens(
+ "a''''b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "\'"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a' '' 'b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, " \' "),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("'abc'", Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "abc"));
+ checkTokens("'abc\ndef'", Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "abc\ndef"));
+ checkTokens(
+ "a'abc\ndef'",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "abc\ndef"));
+ checkTokens(
+ "'abc\ndef'b",
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("a'abc\ndef'b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a'''abc\nd''ef'''b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "\'abc\nd\'ef\'"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ }
+
+ public void testDoubleQuotes() {
+ assertInvalidSql("\"", "Unterminated quote");
+ assertInvalidSql("a\"", "Unterminated quote");
+ assertInvalidSql("a\"\"\"", "Unterminated quote");
+ assertInvalidSql("a\"\"\" ", "Unterminated quote");
+ checkTokens("\"\"", Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, ""));
+ checkTokens("\"\"\"\"", Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "\""));
+ checkTokens(
+ "a\"\"\"\"b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "\""),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("a\"\t\"\"\t\"b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "\t\"\t"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("\"abc\"", Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "abc"));
+ checkTokens(
+ "\"abc\ndef\"",
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "abc\ndef"));
+ checkTokens(
+ "a\"abc\ndef\"",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "abc\ndef"));
+ checkTokens(
+ "\"abc\ndef\"b",
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("a\"abc\ndef\"b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("a\"\"\"abc\nd\"\"ef\"\"\"b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "\"abc\nd\"ef\""),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ }
+
+ public void testBackquotes() {
+ assertInvalidSql("`", "Unterminated quote");
+ assertInvalidSql("a`", "Unterminated quote");
+ assertInvalidSql("a```", "Unterminated quote");
+ assertInvalidSql("a``` ", "Unterminated quote");
+ checkTokens("``", Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, ""));
+ checkTokens("````", Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "`"));
+ checkTokens(
+ "a````b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "`"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a`\t``\t`b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "\t`\t"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("`abc`", Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "abc"));
+ checkTokens(
+ "`abc\ndef`",
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "abc\ndef"));
+ checkTokens(
+ "a`abc\ndef`",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "abc\ndef"));
+ checkTokens(
+ "`abc\ndef`b",
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a`abc\ndef`b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a```abc\nd``ef```b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "`abc\nd`ef`"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ }
+
+ public void testBrackets() {
+ assertInvalidSql("[", "Unterminated quote");
+ assertInvalidSql("a[", "Unterminated quote");
+ assertInvalidSql("a[ ", "Unterminated quote");
+ assertInvalidSql("a[[ ", "Unterminated quote");
+ checkTokens("[]", Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, ""));
+ checkTokens("[[]", Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "["));
+ checkTokens(
+ "a[[]b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "["),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a[\t[\t]b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "\t[\t"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens("[abc]", Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "abc"));
+ checkTokens(
+ "[abc\ndef]",
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "abc\ndef"));
+ checkTokens(
+ "a[abc\ndef]",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "abc\ndef"));
+ checkTokens(
+ "[abc\ndef]b",
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a[abc\ndef]b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "abc\ndef"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ checkTokens(
+ "a[[abc\nd[ef[]b",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "[abc\nd[ef["),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "b"));
+ }
+
+ public void testTokens() {
+ checkTokensRegular("a,abc,a00b,_1,_123,abcdef", "a abc a00b _1 _123 abcdef");
+ checkTokens(
+ "a--\nabc/**/a00b''_1'''ABC'''`_123`abc[d]\"e\"f",
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "abc"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "a00b"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, ""),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "_1"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_SINGLE_QUOTES, "'ABC'"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BACKQUOTES, "_123"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "abc"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_BRACKETS, "d"),
+ Pair.create(SqliteTokenFinder.TYPE_IN_DOUBLE_QUOTES, "e"),
+ Pair.create(SqliteTokenFinder.TYPE_REGULAR, "f"));
+ }
+}