All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zaproxy.zap.db.sql.DbSQL Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.7.0
Show newest version
/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * Copyright 2015 The ZAP Development Team
 *
 * 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 org.zaproxy.zap.db.sql;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.security.InvalidParameterException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedDeque;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.db.Database;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.DatabaseListener;
import org.parosproxy.paros.db.DatabaseServer;
import org.parosproxy.paros.db.DatabaseUnsupportedException;
import org.zaproxy.zap.utils.Stats;

public class DbSQL implements DatabaseListener {

    private static Properties dbProperties = null;
    private static Properties sqlProperties = null;
    private static String dbType = null;

    private static DbSQL singleton = null;
    private static SqlDatabaseServer dbServer = null;

    private static final Logger LOGGER = LogManager.getLogger(DbSQL.class);

    private Map stmtPool = new HashMap<>();

    public static DbSQL getSingleton() {
        if (singleton == null) {
            singleton = new DbSQL();
        }
        return singleton;
    }

    static void reset() {
        dbProperties = null;
        sqlProperties = null;
        dbType = null;
        singleton = null;
        dbServer = null;
    }

    static void setSqlProperties(Properties properties) {
        sqlProperties = properties;
    }

    protected String getDbUser() {
        if (dbProperties == null) {
            throw new IllegalStateException("Database not initialised");
        }
        return dbProperties.getProperty("db.user");
    }

    protected String getDbPassword() {
        if (dbProperties == null) {
            throw new IllegalStateException("Database not initialised");
        }
        return dbProperties.getProperty("db.password");
    }

    protected String getDbUrl() {
        if (dbProperties == null) {
            throw new IllegalStateException("Database not initialised");
        }
        return dbProperties.getProperty("db.url");
    }

    public static String getDbType() {
        return dbType;
    }

    public synchronized Database initDatabase() throws IllegalStateException, DatabaseException {
        if (dbProperties != null) {
            throw new IllegalStateException("Database already initialised");
        }
        File file = new File(Constant.getZapHome() + File.separator + "db", "db.properties");
        if (!file.exists()) {
            file = new File(Constant.getZapInstall() + File.separator + "db", "db.properties");
        }
        if (!file.exists()) {
            throw new DatabaseException("Missing DB properties file: " + file.getAbsolutePath());
        }

        dbProperties = new Properties();
        try (Reader reader = new FileReader(file)) {
            dbProperties.load(reader);
        } catch (IOException e) {
            throw new DatabaseException("I/O error while reading DB properties file.", e);
        }
        dbType = dbProperties.getProperty("db.type");

        // Load the SQL properties
        sqlProperties = new Properties();
        File sqlFile =
                new File(Constant.getZapInstall() + File.separator + "db", dbType + ".properties");
        try (Reader sqlReader = new FileReader(sqlFile)) {
            sqlProperties.load(sqlReader);
        } catch (Exception e) {
            LOGGER.error("No SQL properties file for db type {}", sqlFile.getAbsolutePath());
            throw new DatabaseException(
                    "Missing SQL properties file: " + sqlFile.getAbsolutePath());
        }

        String className = dbProperties.getProperty("db.class");
        Object dbObj;
        try {
            Class dbClass = Class.forName(className);
            dbObj = dbClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new DatabaseException("Failed to create the instance for: " + className, e);
        }

        if (!(dbObj instanceof Database)) {
            throw new DatabaseException(
                    "db.class is not an instance of Database: "
                            + dbObj.getClass().getCanonicalName());
        }
        return (Database) dbObj;
    }

    public static void addSqlProperties(InputStream inStream) throws IOException {
        sqlProperties.load(inStream);
    }

    public static String getSQL(String key) {
        String str = sqlProperties.getProperty(key);
        if (str != null) {
            // trailing spaces can cause havoc ;)
            str = str.trim();
        }
        return str;
    }

    public static String getSQL(String key, Object... params) {
        String str = MessageFormat.format(getSQL(key), params);
        if (str != null) {
            // trailing spaces can cause havoc ;)
            str = str.trim();
        }
        return str;
    }

    public static void setSetValues(PreparedStatement ps, int startPosition, String... values)
            throws SQLException {
        int i = startPosition;

        while (i < values.length) {
            ps.setString(i, values[i]);
            i++;
        }
    }

    public static void setSetValues(PreparedStatement ps, int startPosition, int... values)
            throws SQLException {
        int i = 0;

        while (i < values.length) {
            ps.setInt(startPosition + i, values[i]);
            i++;
        }
    }

    @Override
    public void databaseOpen(DatabaseServer dbServer)
            throws DatabaseException, DatabaseUnsupportedException {

        DbSQL.dbServer = (SqlDatabaseServer) dbServer;

        for (Entry entry : stmtPool.entrySet()) {
            entry.getValue().clear();
        }
        Stats.clear("sqldb.");
    }

    public synchronized SqlPreparedStatementWrapper getPreparedStatement(String key, int... params)
            throws SQLException {
        if (params == null || params.length == 0) {
            return getPreparedStatement(key);
        }
        String internalKey = key + Arrays.toString(params);
        return getStatementPool(internalKey)
                .getPreparedStatement(internalKey, createSQL(key, params));
    }

    public synchronized SqlPreparedStatementWrapper getPreparedStatement(String key)
            throws SQLException {
        return getStatementPool(key).getPreparedStatement(key, getSQL(key));
    }

    private StatementPool getStatementPool(String key) {
        Stats.incCounter("sqldb." + key + ".calls");

        StatementPool sp = this.stmtPool.get(key);
        if (sp == null) {
            synchronized (this) {
                if (!this.stmtPool.containsKey(key)) {
                    this.stmtPool.put(key, new StatementPool());
                }
            }
            sp = this.stmtPool.get(key);
        }

        return sp;
    }

    public void releasePreparedStatement(SqlPreparedStatementWrapper psw) {
        if (psw != null) {
            Stats.incCounter("sqldb." + psw.getKey() + ".time", psw.getTimeTaken());
            this.stmtPool.get(psw.getKey()).releasePreparedStatement(psw);
        }
    }

    private class StatementPool {
        private static final int MAX_FREE_POOL_SIZE = 5;

        private Deque inUsePool = new ConcurrentLinkedDeque<>();
        private Deque freePool = new ConcurrentLinkedDeque<>();

        public SqlPreparedStatementWrapper getPreparedStatement(String key, String sql)
                throws SQLException {
            PreparedStatement ps = freePool.pollFirst();
            if (ps == null) {
                ps = dbServer.getNewConnection().prepareStatement(sql);
                Stats.incCounter("sqldb.conn.openned");
            }
            inUsePool.add(ps);
            Stats.setHighwaterMark("sqldb." + key + ".pool", inUsePool.size());
            return new SqlPreparedStatementWrapper(key, ps);
        }

        public void releasePreparedStatement(SqlPreparedStatementWrapper ps) {
            if (inUsePool.remove(ps.getPs())) {
                if (freePool.size() < MAX_FREE_POOL_SIZE) {
                    freePool.add(ps.getPs());
                } else {
                    try {
                        ps.close();
                        Stats.incCounter("sqldb.conn.closed");
                    } catch (SQLException e) {
                        LOGGER.error("Error closing prepared statement", e);
                    }
                }
            } else {
                LOGGER.error(
                        "Releasing prepared statement not in a pool",
                        new InvalidParameterException());
            }
        }

        public void clear() {
            Iterator iter = inUsePool.iterator();
            while (iter.hasNext()) {
                try {
                    iter.next().close();
                } catch (SQLException e) {
                    // Ignore
                }
            }
            inUsePool.clear();
            iter = freePool.iterator();
            while (iter.hasNext()) {
                try {
                    iter.next().close();
                } catch (SQLException e) {
                    // Ignore
                }
            }
            freePool.clear();
        }
    }

    private static String createSQL(String key, int[] numberOfParams) {
        if (numberOfParams == null || numberOfParams.length == 0) {
            return getSQL(key);
        }

        Object[] parameters = new Object[numberOfParams.length];
        for (int i = 0; i < numberOfParams.length; i++) {
            parameters[i] = StringUtils.repeat("?", ", ", numberOfParams[i]);
        }
        return getSQL(key, parameters);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy