From 3d8031552aad7c8a88181f084dec0fce9747966c Mon Sep 17 00:00:00 2001 From: Yako Date: Mon, 1 Sep 2014 20:48:36 +0800 Subject: [PATCH] improve sqlwall --- .../com/alibaba/druid/wall/WallConfig.java | 9 ++ .../druid/wall/spi/MySqlWallVisitor.java | 82 +------------ .../druid/wall/spi/WallVisitorUtils.java | 111 +++++++++++++++--- .../druid/wall/violation/ErrorCode.java | 1 + .../bvt/filter/wall/MySqlWallTest_hint.java | 23 ++++ 5 files changed, 128 insertions(+), 98 deletions(-) diff --git a/src/main/java/com/alibaba/druid/wall/WallConfig.java b/src/main/java/com/alibaba/druid/wall/WallConfig.java index f17a733da2..1d67b4f236 100644 --- a/src/main/java/com/alibaba/druid/wall/WallConfig.java +++ b/src/main/java/com/alibaba/druid/wall/WallConfig.java @@ -41,6 +41,7 @@ public class WallConfig implements WallConfigMBean { private boolean alterTableAllow = true; private boolean renameTableAllow = true; private boolean hintAllow = true; + private boolean lockTableAllow = true; private boolean conditionAndAlwayTrueAllow = false; private boolean conditionAndAlwayFalseAllow = false; @@ -752,4 +753,12 @@ public class WallConfig implements WallConfigMBean { this.inited = inited; } + public boolean isLockTableAllow() { + return lockTableAllow; + } + + public void setLockTableAllow(boolean lockTableAllow) { + this.lockTableAllow = lockTableAllow; + } + } diff --git a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java index 24139ee1b0..916477d3bb 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java @@ -445,87 +445,7 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi @Override public boolean visit(SQLCommentHint x) { - if(!provider.getConfig().isHintAllow()) { - addViolation(new IllegalSQLObjectViolation(ErrorCode.EVIL_HINTS, "hint not allow", SQLUtils.toMySqlString(x))); - return true; - } - - String text = x.getText(); - text = text.trim(); - if (text.startsWith("!")) { - text = text.substring(1); - } - - if (text.length() == 0) { - return true; - } - - if (Character.isDigit(text.charAt(0))) { - addViolation(new IllegalSQLObjectViolation(ErrorCode.EVIL_HINTS, "evil hints", SQLUtils.toMySqlString(x))); - } - - text = text.toLowerCase(); - - for (int i = 0; i < text.length(); ++i) { - char ch = text.charAt(i); - switch (ch) { - case ';': - case '>': - case '=': - case '<': - case '&': - case '|': - case '^': - case '\n': - case '.': - case '(': - addViolation(new IllegalSQLObjectViolation(ErrorCode.EVIL_HINTS, "evil hints", - SQLUtils.toMySqlString(x))); - default: - break; - } - } - - if (text.indexOf("or") != -1 // - || text.indexOf("and") != -1 // - || text.indexOf("union") != -1 // - || text.indexOf("xor") != -1 // - - || text.indexOf("select") != -1 // - || text.indexOf("delete") != -1 // - || text.indexOf("insert") != -1 // - || text.indexOf("update") != -1 // - || text.indexOf("into") != -1 // - - || text.indexOf("create") != -1 // - || text.indexOf("drop") != -1 // - || text.indexOf("alter") != -1 // - || text.indexOf("truncate") != -1 // - - || text.indexOf("information_schema") != -1 // - || text.indexOf("mysql") != -1 // - || text.indexOf("performance_schema") != -1 // - - || text.indexOf("sleep") != -1 // - || text.indexOf("benchmark") != -1 // - || text.indexOf("load_file") != -1 // - || text.indexOf("version") != -1 // - || text.indexOf("database") != -1 // - || text.indexOf("schema") != -1 // - || text.indexOf("system_user") != -1 // - || text.indexOf("session_user") != -1 // - || text.indexOf("current_user") != -1 // - || text.indexOf("user") != -1 // - || text.indexOf("xmltype") != -1 // - || text.indexOf("receive_message") != -1 // - - || text.indexOf("version_compile_os") != -1 // - || text.indexOf("basedir") != -1 // - || text.indexOf("datadir") != -1 // - ) { - addViolation(new IllegalSQLObjectViolation(ErrorCode.EVIL_HINTS, "evil hints", SQLUtils.toMySqlString(x))); - } - + WallVisitorUtils.check(this, x); return true; } diff --git a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java index 63aadcab69..929c14973b 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -15,7 +15,22 @@ */ package com.alibaba.druid.wall.spi; +import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; +import java.util.Stack; + import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObject; @@ -84,6 +99,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDescribeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLockTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; @@ -92,11 +108,13 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSetNamesStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowGrantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; +import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.druid.sql.visitor.ExportParameterVisitor; import com.alibaba.druid.sql.visitor.SQLEvalVisitor; import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; @@ -116,25 +134,14 @@ import com.alibaba.druid.wall.WallVisitor; import com.alibaba.druid.wall.violation.ErrorCode; import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Set; -import java.util.Stack; - -import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE; - public class WallVisitorUtils { - private final static Log LOG = LogFactory.getLog(WallVisitorUtils.class); + private final static Log LOG = LogFactory.getLog(WallVisitorUtils.class); - public final static String HAS_TRUE_LIKE = "hasTrueLike"; + public final static String HAS_TRUE_LIKE = "hasTrueLike"; + + public final static String[] whiteHints = { "LOCAL", "TEMPORARY", "SQL_NO_CACHE", "SQL_CACHE", "HIGH_PRIORITY", + "LOW_PRIORITY", "STRAIGHT_JOIN", "SQL_BUFFER_RESULT", "SQL_BIG_RESULT", "SQL_SMALL_RESULT", "DELAYED" }; public static void check(WallVisitor visitor, SQLInListExpr x) { @@ -2405,14 +2412,84 @@ public class WallVisitorUtils { allow = config.isHintAllow(); denyMessage = "hint not allow"; errorCode = ErrorCode.HINT_NOT_ALLOW; + } else if (x instanceof MySqlLockTableStatement) { + allow = config.isLockTableAllow(); + denyMessage = "lock table not allow"; + errorCode = ErrorCode.LOCK_TABLE_NOT_ALLOW; } else { allow = config.isNoneBaseStatementAllow(); errorCode = ErrorCode.NONE_BASE_STATEMENT_NOT_ALLOW; denyMessage = x.getClass() + " not allow"; - } + } if (!allow) { addViolation(visitor, errorCode, denyMessage, x); } } + + public static void check(WallVisitor visitor, SQLCommentHint x) { + if (!visitor.getConfig().isHintAllow()) { + addViolation(visitor, ErrorCode.EVIL_HINTS, "hint not allow", x); + return; + } + + String text = x.getText(); + text = text.trim(); + if (text.startsWith("!")) { + text = text.substring(1); + } + + if (text.length() == 0) { + return; + } + + int pos = 0; + for (; pos < text.length(); pos++) { + char ch = text.charAt(pos); + if (ch >= '0' && ch <= '9') { + continue; + } else { + break; + } + } + + if (pos == 5) { + text = text.substring(5); + text = text.trim(); + } + + text = text.toUpperCase(); + + boolean isWhite = false; + for (String hint : whiteHints) { + if (text.equals(hint)) { + isWhite = true; + break; + } + } + + if (!isWhite) { + if (text.startsWith("FORCE INDEX") || text.startsWith("IGNORE INDEX")) { + isWhite = true; + } + } + + if(!isWhite) { + if (text.startsWith("SET")) { + SQLStatementParser parser = new MySqlStatementParser(text); + List statementList = parser.parseStatementList(); + if (statementList != null && statementList.size() > 0) { + SQLStatement statement = statementList.get(0); + if (statement instanceof SQLSetStatement || statement instanceof MySqlSetCharSetStatement + || statement instanceof MySqlSetNamesStatement) { + isWhite = true; + } + } + } + } + + if (!isWhite) { + addViolation(visitor, ErrorCode.EVIL_HINTS, "hint not allow", x); + } + } } diff --git a/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java b/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java index 08880a5359..f6d0582e61 100644 --- a/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java +++ b/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java @@ -47,6 +47,7 @@ public interface ErrorCode { public final static int DROP_TABLE_NOT_ALLOW = 1103; public final static int COMMENT_STATEMENT_NOT_ALLOW = 1104; public final static int RENAME_TABLE_NOT_ALLOW = 1105; + public final static int LOCK_TABLE_NOT_ALLOW = 1105; public final static int LIMIT_ZERO = 2200; public final static int MULTI_STATEMENT = 2201; diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest_hint.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest_hint.java index 943fc2a24d..fd0762bf73 100644 --- a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest_hint.java +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest_hint.java @@ -53,5 +53,28 @@ public class MySqlWallTest_hint extends TestCase { Assert.assertTrue(WallUtils.isValidateMySql(sql, config)); } + public void test_true_4() throws Exception { + WallConfig config = new WallConfig(); + config.setHintAllow(true); + config.setMultiStatementAllow(true); + String sql = "LOCK TABLES `m_rpt_adgroupeffect` READ /*!32311 LOCAL */"; + Assert.assertTrue(WallUtils.isValidateMySql(sql, config)); // + } + + public void test_true_5() throws Exception { + WallConfig config = new WallConfig(); + config.setHintAllow(true); + config.setMultiStatementAllow(true); + String sql = "DROP TABLE IF EXISTS `item_similarity`;"// + + "\n/*!40101 SET @saved_cs_client = @@character_set_client */;"// + + "\n/*!40101 SET character_set_client = utf8 */;" // + + "\nCREATE TABLE `item_similarity` ("// + + " `id` bigint(20) unsigned NOT NULL, "// + + " `sellerId` bigint(20) DEFAULT NULL,"// + + " PRIMARY KEY (`id`)" // + + " ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"// + + " \n/*!40101 SET character_set_client = @saved_cs_client */;"; + Assert.assertTrue(WallUtils.isValidateMySql(sql, config)); // + } }