diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGExportParameterVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGExportParameterVisitor.java new file mode 100644 index 0000000000..c7b719223c --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGExportParameterVisitor.java @@ -0,0 +1,87 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * 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.alibaba.druid.sql.dialect.postgresql.visitor; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLInListExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.visitor.ExportParameterVisitor; +import com.alibaba.druid.sql.visitor.ExportParameterVisitorUtils; + +public class PGExportParameterVisitor extends PGASTVisitorAdapter implements ExportParameterVisitor { + + private final List parameters; + + public PGExportParameterVisitor() { + this(new ArrayList()); + } + + public PGExportParameterVisitor(List parameters){ + this.parameters = parameters; + } + + public List getParameters() { + return parameters; + } + + @Override + public boolean visit(SQLSelectItem x) { + return false; + } + + @Override + public boolean visit(SQLOrderBy x) { + return false; + } + + @Override + public boolean visit(SQLSelectGroupByClause x) { + return false; + } + + @Override + public boolean visit(SQLMethodInvokeExpr x) { + ExportParameterVisitorUtils.exportParamterAndAccept(this.parameters, x.getParameters()); + + return true; + } + + @Override + public boolean visit(SQLInListExpr x) { + ExportParameterVisitorUtils.exportParamterAndAccept(this.parameters, x.getTargetList()); + + return true; + } + + @Override + public boolean visit(SQLBetweenExpr x) { + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + return true; + } + + public boolean visit(SQLBinaryOpExpr x) { + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + return true; + } + +} diff --git a/src/main/java/com/alibaba/druid/wall/WallConfig.java b/src/main/java/com/alibaba/druid/wall/WallConfig.java index 725c5727df..d40678c059 100644 --- a/src/main/java/com/alibaba/druid/wall/WallConfig.java +++ b/src/main/java/com/alibaba/druid/wall/WallConfig.java @@ -45,7 +45,7 @@ public class WallConfig implements WallConfigMBean { private boolean insertAllow = true; private boolean mergeAllow = true; private boolean minusAllow = false; - private boolean intersectAllow = false; + private boolean intersectAllow = false; private boolean multiStatementAllow = false; @@ -73,6 +73,10 @@ public class WallConfig implements WallConfigMBean { protected final Set denyVariants = new ConcurrentSkipListSet(); protected final Set denyObjects = new ConcurrentSkipListSet(); + protected final Set permitFunctions = new ConcurrentSkipListSet(); + protected final Set permitTables = new ConcurrentSkipListSet(); + protected final Set permitSchemas = new ConcurrentSkipListSet(); + protected final Set readOnlyTables = new ConcurrentSkipListSet(); private String dir; @@ -91,11 +95,11 @@ public class WallConfig implements WallConfigMBean { public WallConfig(){ } - + public boolean isIntersectAllow() { return intersectAllow; } - + public void setIntersectAllow(boolean intersectAllow) { this.intersectAllow = intersectAllow; } @@ -204,6 +208,10 @@ public class WallConfig implements WallConfigMBean { loadResource(this.denyTables, dir + "/deny-table.txt"); loadResource(this.denyObjects, dir + "/deny-object.txt"); loadResource(this.readOnlyTables, dir + "/readonly-table.txt"); + + loadResource(this.permitFunctions, dir + "/permit-function.txt"); + loadResource(this.permitTables, dir + "/permit-table.txt"); + loadResource(this.permitSchemas, dir + "/permit-schema.txt"); } public boolean isNoneBaseStatementAllow() { @@ -444,6 +452,18 @@ public class WallConfig implements WallConfigMBean { return this.readOnlyTables.contains(tableName); } + public Set getPermitFunctions() { + return permitFunctions; + } + + public Set getPermitTables() { + return permitTables; + } + + public Set getPermitSchemas() { + return permitSchemas; + } + public boolean isMustParameterized() { return mustParameterized; } diff --git a/src/main/java/com/alibaba/druid/wall/WallFilter.java b/src/main/java/com/alibaba/druid/wall/WallFilter.java index 50fe8a003a..b73a4ae1ff 100644 --- a/src/main/java/com/alibaba/druid/wall/WallFilter.java +++ b/src/main/java/com/alibaba/druid/wall/WallFilter.java @@ -37,6 +37,7 @@ import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.JdbcUtils; import com.alibaba.druid.wall.spi.MySqlWallProvider; import com.alibaba.druid.wall.spi.OracleWallProvider; +import com.alibaba.druid.wall.spi.PGWallProvider; import com.alibaba.druid.wall.spi.SQLServerWallProvider; import com.alibaba.druid.wall.violation.SyntaxErrorViolation; @@ -90,6 +91,12 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean { } provider = new SQLServerWallProvider(config); + } else if (JdbcUtils.POSTGRESQL.equals(dbType)) { + if (config == null) { + config = new WallConfig(PGWallProvider.DEFAULT_CONFIG_DIR); + } + + provider = new PGWallProvider(config); } else { throw new IllegalStateException("dbType not support : " + dbType + ", url " + dataSource.getUrl()); } diff --git a/src/main/java/com/alibaba/druid/wall/spi/PGWallProvider.java b/src/main/java/com/alibaba/druid/wall/spi/PGWallProvider.java new file mode 100644 index 0000000000..62c91f1dc7 --- /dev/null +++ b/src/main/java/com/alibaba/druid/wall/spi/PGWallProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * 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.alibaba.druid.wall.spi; + +import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGExportParameterVisitor; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.visitor.ExportParameterVisitor; +import com.alibaba.druid.util.JdbcConstants; +import com.alibaba.druid.wall.WallConfig; +import com.alibaba.druid.wall.WallProvider; +import com.alibaba.druid.wall.WallVisitor; + + +public class PGWallProvider extends WallProvider { + + public final static String DEFAULT_CONFIG_DIR = "META-INF/druid/wall/postgres"; + + public PGWallProvider(){ + this(new WallConfig(DEFAULT_CONFIG_DIR)); + } + + public PGWallProvider(WallConfig config){ + super(config, JdbcConstants.POSTGRESQL); + } + + @Override + public SQLStatementParser createParser(String sql) { + return new PGSQLStatementParser(sql); + } + + @Override + public WallVisitor createWallVisitor() { + return new PGWallVisitor(this); + } + + @Override + public ExportParameterVisitor createExportParameterVisitor() { + return new PGExportParameterVisitor(); + } + +} diff --git a/src/main/java/com/alibaba/druid/wall/spi/PGWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/PGWallVisitor.java new file mode 100644 index 0000000000..61b4f263e6 --- /dev/null +++ b/src/main/java/com/alibaba/druid/wall/spi/PGWallVisitor.java @@ -0,0 +1,228 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * 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.alibaba.druid.wall.spi; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLObject; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLInListExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; +import com.alibaba.druid.sql.ast.statement.SQLCallStatement; +import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; +import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +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.OracleSelectTableReference; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitorAdapter; +import com.alibaba.druid.wall.Violation; +import com.alibaba.druid.wall.WallConfig; +import com.alibaba.druid.wall.WallProvider; +import com.alibaba.druid.wall.WallVisitor; +import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation; + +public class PGWallVisitor extends PGASTVisitorAdapter implements WallVisitor { + + private final WallConfig config; + private final WallProvider provider; + private final List violations = new ArrayList(); + + public PGWallVisitor(WallProvider provider){ + this.config = provider.getConfig(); + this.provider = provider; + } + + public WallProvider getProvider() { + return provider; + } + + public WallConfig getConfig() { + return config; + } + + public void addViolation(Violation violation) { + this.violations.add(violation); + } + + public List getViolations() { + return violations; + } + + public boolean visit(SQLIdentifierExpr x) { + String name = x.getName(); + name = WallVisitorUtils.form(name); + if (config.isVariantCheck() && config.getDenyVariants().contains(name)) { + getViolations().add(new IllegalSQLObjectViolation(toSQL(x))); + } + return true; + } + + public boolean visit(SQLPropertyExpr x) { + WallVisitorUtils.check(this, x); + return true; + } + + public boolean visit(SQLInListExpr x) { + WallVisitorUtils.check(this, x); + return true; + } + + public boolean visit(SQLBinaryOpExpr x) { + WallVisitorUtils.check(this, x); + return true; + } + + @Override + public boolean visit(SQLMethodInvokeExpr x) { + WallVisitorUtils.checkFunction(this, x); + + return true; + } + + public boolean visit(OracleSelectTableReference x) { + return WallVisitorUtils.check(this, x); + } + + public boolean visit(SQLExprTableSource x) { + WallVisitorUtils.check(this, x); + + if (x.getExpr() instanceof SQLName) { + return false; + } + + return true; + } + + public boolean visit(SQLSelectGroupByClause x) { + WallVisitorUtils.checkHaving(this, x.getHaving()); + return true; + } + + @Override + public boolean visit(SQLSelectQueryBlock x) { + WallVisitorUtils.checkSelelct(this, x); + + return true; + } + + @Override + public boolean visit(SQLUnionQuery x) { + WallVisitorUtils.checkUnion(this, x); + + return true; + } + + @Override + public String toSQL(SQLObject obj) { + return SQLUtils.toOracleString(obj); + } + + @Override + public boolean isDenyTable(String name) { + if (!config.isTableCheck()) { + return false; + } + + name = WallVisitorUtils.form(name); + if (name.startsWith("v$") || name.startsWith("v_$")) { + return true; + } + return !this.provider.checkDenyTable(name); + } + + public void preVisit(SQLObject x) { + if (!(x instanceof SQLStatement)) { + return; + } + + if (config.isNoneBaseStatementAllow()) { + return; + } + + boolean allow = false; + if (x instanceof SQLInsertStatement) { + allow = true; + } else if (x instanceof SQLSelectStatement) { + allow = true; + } else if (x instanceof SQLDeleteStatement) { + allow = true; + } else if (x instanceof SQLUpdateStatement) { + allow = true; + } else if (x instanceof OracleMultiInsertStatement) { + allow = true; + } else if (x instanceof OracleMergeStatement) { + allow = true; + } else if (x instanceof SQLCallStatement) { + allow = true; + } else if (x instanceof SQLTruncateStatement) { + allow = config.isTruncateAllow(); + } + + if (!allow) { + violations.add(new IllegalSQLObjectViolation(toSQL(x))); + } + } + + @Override + public boolean visit(SQLSelectStatement x) { + if (!config.isSelelctAllow()) { + this.getViolations().add(new IllegalSQLObjectViolation(this.toSQL(x))); + return false; + } + + return true; + } + + @Override + public boolean visit(SQLInsertStatement x) { + WallVisitorUtils.checkInsert(this, x); + + return true; + } + + @Override + public boolean visit(SQLDeleteStatement x) { + WallVisitorUtils.checkDelete(this, x); + return true; + } + + @Override + public boolean visit(SQLUpdateStatement x) { + WallVisitorUtils.checkUpdate(this, x); + + return true; + } + + @Override + public boolean visit(SQLSelectItem x) { + WallVisitorUtils.check(this, x); + return true; + } +} diff --git a/src/main/resources/META-INF/druid/wall/mysql/permit-function.txt b/src/main/resources/META-INF/druid/wall/mysql/permit-function.txt new file mode 100644 index 0000000000..518f788238 --- /dev/null +++ b/src/main/resources/META-INF/druid/wall/mysql/permit-function.txt @@ -0,0 +1,219 @@ +abs +acos +adddate +addtime +aes_decrypt +aes_encrypt +ascii +asin +atan +atan2 +avg +bin +binary +bit_and +bit_count +bit_length +bit_or +bit_xor +cast +ceil +ceiling +char_length +char +character_length +charset +coalesce +coercibility +collation +compress +concat_ws +concat +conv +convert_tz +convert +cos +cot +count(distinct) +count +crc32 +curdate +current_date +current_time +current_timestamp +curtime +date_add +date_format +date_sub +date +datediff +day +dayname +dayofmonth +dayofweek +dayofyear +decode +default +degrees +des_decrypt +des_encrypt +div +elt +encode +encrypt +exp +export_set +extract +extractvalue +field +find_in_set +floor +format +found_rows +from_base64 +from_days +from_unixtime +get_format +greatest +group_concat +gtid_subset +gtid_subtract +hex +hour +if +ifnull +inet_aton +inet_ntoa +inet6_aton +inet6_ntoa +insert +instr +interval +is_free_lock +is_ipv4_compat +is_ipv4_mapped +is_ipv4 +is_ipv6 +is_used_lock +last_day +last_insert_id +lcase +least +left +length +ln +localtime +localtimestamp +locate +log10 +log2 +log +lower +lpad +ltrim +make_set +makedate +maketime +master_pos_wait +match +max +md5 +microsecond +mid +min +minute +mod +month +monthname +name_const +not +now +nullif +oct +octet_length +old_password +ord +password +period_add +period_diff +pi +position +pow +power +procedure +quarter +quote +radians +rand +regexp +release_lock +repeat +replace +reverse +right +rlike +round +row_count +rpad +rtrim +sec_to_time +second +sha1 +sha2 +sign +sin +sleep +soundex +sounds +space +sqrt +std +stddev_pop +stddev_samp +stddev +str_to_date +strcmp +subdate +substr +substring_index +substring +subtime +sum +sysdate +tan +time_format +time_to_sec +time +timediff +timestamp +timestampadd +timestampdiff +to_base64 +to_days +to_seconds +trim +truncate +ucase +uncompress +uncompressed_length +unhex +unix_timestamp +updatexml +upper +utc_date +utc_time +utc_timestamp +uuid_short +uuid +validate_password_strength +values +var_pop +var_samp +variance +wait_until_sql_thread_after_gtids +week +weekday +weekofyear +weight_string +year +yearweek \ No newline at end of file diff --git a/src/main/resources/META-INF/druid/wall/oracle/permit-function.txt b/src/main/resources/META-INF/druid/wall/oracle/permit-function.txt new file mode 100644 index 0000000000..525d9e01bf --- /dev/null +++ b/src/main/resources/META-INF/druid/wall/oracle/permit-function.txt @@ -0,0 +1,159 @@ +abs +acos +asin +atan +atan2 +bitand +ceil +cos +cosh +exp +floor +ln +log +mod +nanvl +power +remainder +round +sign +sin +sinh +sqrt +tan +tanh +trunc +width_bucket + +CHR +CONCAT +INITCAP +LOWER +LPAD +LTRIM +NLS_INITCAP +NLS_LOWER +NLSSORT +NLS_UPPER +REGEXP_REPLACE +REGEXP_SUBSTR +REPLACE +RPAD +RTRIM +SOUNDEX +SUBSTR +TRANSLATE +TREAT +TRIM +UPPER + +NLS_CHARSET_DECL_LEN +NLS_CHARSET_ID +NLS_CHARSET_NAME + +ASCII +INSTR +LENGTH +REGEXP_INSTR + +ADD_MONTHS +CURRENT_DATE +CURRENT_TIMESTAMP +DBTIMEZONE +EXTRACT +FROM_TZ +LAST_DAY +LOCALTIMESTAMP +MONTHS_BETWEEN +NEW_TIME +NEXT_DAY +NUMTODSINTERVAL +NUMTOYMINTERVAL +ROUND +SESSIONTIMEZONE +SYS_EXTRACT_UTC +SYSDATE +SYSTIMESTAMP +TO_CHAR +TO_TIMESTAMP +TO_TIMESTAMP_TZ +TO_DSINTERVAL +TO_YMINTERVAL +TRUNC +TZ_OFFSET + +GREATEST +LEAST + +ASCIISTR +BIN_TO_NUM +CAST +CHARTOROWID +COMPOSE +CONVERT +DECOMPOSE +HEXTORAW +NUMTODSINTERVAL +NUMTOYMINTERVAL +RAWTOHEX +RAWTONHEX +ROWIDTOCHAR +ROWIDTONCHAR +SCN_TO_TIMESTAMP +TIMESTAMP_TO_SCN +TO_BINARY_DOUBLE +TO_BINARY_FLOAT +TO_CHAR +TO_CLOB +TO_DATE +TO_DSINTERVAL +TO_LOB +TO_MULTI_BYTE +TO_NCHAR +TO_NCLOB +TO_NUMBER +TO_DSINTERVAL +TO_SINGLE_BYTE +TO_TIMESTAMP +TO_TIMESTAMP_TZ +TO_YMINTERVAL +TO_YMINTERVAL +TRANSLATE +UNISTR + +BFILENAME +EMPTY_BLOB +EMPTY_CLOB + +CARDINALITY +COLLECT +POWERMULTISET +POWERMULTISET_BY_CARDINALITY +SET + +SYS_CONNECT_BY_PATH + +CLUSTER_ID +CLUSTER_PROBABILITY +CLUSTER_SET +FEATURE_ID +FEATURE_SET +FEATURE_VALUE +PREDICTION +PREDICTION_BOUNDS +PREDICTION_COST +PREDICTION_DETAILS +PREDICTION_PROBABILITY +PREDICTION_SET + +DECODE +DUMP +ORA_HASH +VSIZE + +COALESCE +LNNVL +NULLIF +NVL +NVL2 +