diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java index 8810b87d72..6e0e6ac55e 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java @@ -30,17 +30,24 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class MySqlDeleteStatement extends SQLDeleteStatement { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private boolean lowPriority = false; - private boolean quick = false; - private boolean ignore = false; + private boolean lowPriority = false; + private boolean quick = false; + private boolean ignore = false; - private SQLTableSource from; - private SQLTableSource using; - private SQLOrderBy orderBy; - private Limit limit; - private final List tableSources = new ArrayList(); + private SQLTableSource from; + private SQLTableSource using; + private SQLOrderBy orderBy; + private Limit limit; + private final List tableSources = new ArrayList(); + + public SQLExprTableSource getTableSource() { + if (tableSources.size() > 0) { + return tableSources.get(0); + } + return null; + } public boolean isLowPriority() { return lowPriority; @@ -84,7 +91,7 @@ public class MySqlDeleteStatement extends SQLDeleteStatement { } return (SQLName) tableSources.get(0).getExpr(); } - + public void setTableSource(SQLExprTableSource tableSource) { if (tableSources.size() == 0) { tableSources.add(tableSource); diff --git a/src/main/java/com/alibaba/druid/wall/WallConfig.java b/src/main/java/com/alibaba/druid/wall/WallConfig.java index a7bec63519..aae82a972b 100644 --- a/src/main/java/com/alibaba/druid/wall/WallConfig.java +++ b/src/main/java/com/alibaba/druid/wall/WallConfig.java @@ -41,6 +41,8 @@ public class WallConfig { protected final Set permitVariants = new HashSet(); protected final Set permitObjects = new HashSet(); + protected final Set readOnlyTables = new HashSet(); + private String dir; private boolean inited; @@ -256,6 +258,10 @@ public class WallConfig { return permitObjects; } + public Set getReadOnlyTables() { + return readOnlyTables; + } + public boolean isPermitObjects(String name) { if (!objectCheck) { return false; @@ -269,7 +275,7 @@ public class WallConfig { if (!schemaCheck) { return false; } - + name = WallVisitorUtils.form(name); return this.permitSchemas.contains(name); } @@ -278,7 +284,7 @@ public class WallConfig { if (!functionCheck) { return false; } - + name = WallVisitorUtils.form(name); return this.permitFunctions.contains(name); } 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 9433fc35b3..33af158a56 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java @@ -107,10 +107,7 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi @Override public boolean visit(SQLInsertStatement x) { - if (!config.isInsertAllow()) { - this.getViolations().add(new IllegalSQLObjectViolation(this.toSQL(x))); - return false; - } + WallVisitorUtils.checkInsert(this, x); return true; } diff --git a/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java index bd577b6ea4..8e3ab83ad7 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java @@ -24,6 +24,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement; 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.oracle.ast.stmt.OracleMultiInsertStatement.InsertIntoClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement; @@ -155,7 +156,7 @@ public class OracleWallVisitor extends OracleASTVIsitorAdapter implements WallVi } else if (x instanceof OracleMergeStatement) { } else if (x instanceof SQLTruncateStatement) { if (!config.isTruncateAllow()) { - violations.add(new IllegalSQLObjectViolation(toSQL(x))); + violations.add(new IllegalSQLObjectViolation(toSQL(x))); } } else { violations.add(new IllegalSQLObjectViolation(toSQL(x))); @@ -179,13 +180,17 @@ public class OracleWallVisitor extends OracleASTVIsitorAdapter implements WallVi @Override public boolean visit(SQLInsertStatement x) { - if (!config.isInsertAllow()) { - this.getViolations().add(new IllegalSQLObjectViolation(this.toSQL(x))); - return false; - } + WallVisitorUtils.checkInsert(this, x); return true; } + + @Override + public boolean visit(InsertIntoClause x) { + WallVisitorUtils.checkInsert(this, x); + + return true; + } @Override public boolean visit(OracleMultiInsertStatement x) { 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 8ccfba087a..a82e4f9de6 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -29,6 +29,8 @@ import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLInsertInto; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; @@ -56,7 +58,19 @@ public class WallVisitorUtils { } } + public static void checkInsert(WallVisitor visitor, SQLInsertInto x) { + checkReadOnly(visitor, x.getTableSource()); + + if (!visitor.getConfig().isInsertAllow()) { + visitor.getViolations().add(new IllegalSQLObjectViolation(visitor.toSQL(x))); + } + } + public static void checkSelelct(WallVisitor visitor, SQLSelectQueryBlock x) { + if (x.getInto() != null) { + checkReadOnly(visitor, x.getInto()); + } + if (!visitor.getConfig().isSelectIntoAllow() && x.getInto() != null) { addViolation(visitor, x); return; @@ -98,6 +112,8 @@ public class WallVisitorUtils { } public static void checkDelete(WallVisitor visitor, SQLDeleteStatement x) { + checkReadOnly(visitor, x.getTableSource()); + if (!visitor.getConfig().isDeleteAllow()) { addViolation(visitor, x); return; @@ -112,7 +128,33 @@ public class WallVisitorUtils { } } + public static void checkReadOnly(WallVisitor visitor, SQLTableSource tableSource) { + if (tableSource instanceof SQLExprTableSource) { + String tableName = null; + SQLExpr tableNameExpr = ((SQLExprTableSource) tableSource).getExpr(); + if (tableNameExpr instanceof SQLIdentifierExpr) { + tableName = tableNameExpr.toString(); + } else if (tableNameExpr instanceof SQLPropertyExpr) { + tableName = ((SQLPropertyExpr) tableNameExpr).getName(); + } + + if (tableName != null) { + tableName = form(tableName); + if (visitor.getConfig().getReadOnlyTables().contains(tableName)) { + addViolation(visitor, tableSource); + } + } + } else if (tableSource instanceof SQLJoinTableSource) { + SQLJoinTableSource join = (SQLJoinTableSource) tableSource; + + checkReadOnly(visitor, join.getLeft()); + checkReadOnly(visitor, join.getRight()); + } + } + public static void checkUpdate(WallVisitor visitor, SQLUpdateStatement x) { + checkReadOnly(visitor, x.getTableSource()); + if (!visitor.getConfig().isUpdateAllow()) { addViolation(visitor, x); return; @@ -455,7 +497,7 @@ public class WallVisitorUtils { if (!visitor.getConfig().isSelectUnionCheck()) { return; } - + if (WallVisitorUtils.queryBlockFromIsNull(x.getLeft()) || WallVisitorUtils.queryBlockFromIsNull(x.getRight())) { addViolation(visitor, x); } diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/WallReadOnlyTest.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallReadOnlyTest.java new file mode 100644 index 0000000000..3c09fd3dbc --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallReadOnlyTest.java @@ -0,0 +1,39 @@ +package com.alibaba.druid.bvt.filter.wall; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.wall.WallConfig; +import com.alibaba.druid.wall.WallUtils; + +/** + * 这个场景,检测可疑的Having条件 + * @author wenshao + * + */ +public class WallReadOnlyTest extends TestCase { + private WallConfig config = new WallConfig(); + + protected void setUp() throws Exception { + config.getReadOnlyTables().add("members"); + } + private String sql = "SELECT F1, F2 members"; + private String insert_sql = "INSERT INTO members (FID, FNAME) VALUES (?, ?)"; + private String update_sql = "UPDATE members SET FNAME = ? WHERe FID = ?"; + private String delete_sql = "DELETE FROM members WHERE FID = ?"; + + public void testMySql() throws Exception { + Assert.assertTrue(WallUtils.isValidateMySql(sql, config)); + Assert.assertFalse(WallUtils.isValidateMySql(insert_sql, config)); + Assert.assertFalse(WallUtils.isValidateMySql(update_sql, config)); + Assert.assertFalse(WallUtils.isValidateMySql(delete_sql, config)); + } + + public void testORACLE() throws Exception { + Assert.assertTrue(WallUtils.isValidateOracle(sql, config)); + Assert.assertFalse(WallUtils.isValidateOracle(insert_sql, config)); + Assert.assertFalse(WallUtils.isValidateOracle(update_sql, config)); + Assert.assertFalse(WallUtils.isValidateOracle(delete_sql, config)); + } +}