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

org.parosproxy.paros.model.Model 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.

The newest version!
/*
 *
 * Paros and its related class files.
 *
 * Paros is an HTTP/HTTPS proxy for assessing web application security.
 * Copyright (C) 2003-2004 Chinotec Technologies Company
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Clarified Artistic License
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Clarified Artistic License for more details.
 *
 * You should have received a copy of the Clarified Artistic License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
// ZAP: 2012/02/18 Rationalised session handling
// ZAP: 2012/04/23 Added @Override annotation to the appropriate method.
// ZAP: 2012/05/02 Added the method createSingleton and changed the method
// getSingleton to use it.
// ZAP: 2012/06/11 Changed the method copySessionDb to call the method
// Database.close(boolean, boolean).
// ZAP: 2012/08/08 Check if file exist.
// ZAP: 2012/10/02 Issue 385: Added support for Contexts
// ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments
// ZAP: 2013/04/16 Issue 638: Persist and snapshot sessions instead of saving them
// ZAP: 2013/08/27 Issue 772: Restructuring of Saving/Loading Context Data
// ZAP: 2013/11/16 Issue 881: Fail immediately if zapdb.script file is not found
// ZAP: 2013/12/03 Issue 933: Automatically determine install dir
// ZAP: 2014/01/17 Issue 987: Allow arbitrary config file values to be set via the command line
// ZAP: 2014/07/15 Issue 1265: Context import and export
// ZAP: 2015/02/09 Issue 1525: Introduce a database interface layer to allow for alternative
// implementations
// ZAP: 2015/04/02 Issue 321: Support multiple databases
// ZAP: 2016/02/10 Issue 1958: Allow to disable database (HSQLDB) log
// ZAP: 2016/03/22 Allow to remove ContextDataFactory
// ZAP: 2016/03/23 Issue 2331: Custom Context Panels not show in existing contexts after
// installation of add-on
// ZAP: 2016/06/10 Do not clean up the database if the current session does not require it
// ZAP: 2016/07/05 Issue 2218: Persisted Sessions don't save unconfigured Default Context
// ZAP: 2017/06/07 Allow to persist the session properties (e.g. name, description).
// ZAP: 2018/03/27 Validate that context and configurations for ContextDataFactory are not null.
// ZAP: 2018/07/19 Fallback to bundled zapdb.script file.
// ZAP: 2018/08/15 Deprecated addSessionListener
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2020/09/15 Added the VariantFactory
// ZAP: 2020/10/14 Allow to set a singleton Model for tests.
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2022/09/21 Use format specifiers instead of concatenation when logging.
// ZAP: 2023/01/10 Tidy up logger.
package org.parosproxy.paros.model;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
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.paros.ParosDatabase;
import org.xml.sax.SAXException;
import org.zaproxy.zap.control.ControlOverrides;
import org.zaproxy.zap.db.sql.DbSQL;
import org.zaproxy.zap.extension.ascan.VariantFactory;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.model.ContextDataFactory;

public class Model {

    private static Model model = null;

    private static final String DBNAME_TEMPLATE = Constant.DBNAME_TEMPLATE;
    // private static final String DBNAME_UNTITLED = Constant.DBNAME_UNTITLED;
    private String DBNAME_UNTITLED = Constant.getInstance().DBNAME_UNTITLED;
    private static int DBNAME_COPY = 1;

    private Session session = null;
    private OptionsParam optionsParam = null;
    private Database db = null;
    private String currentDBNameUntitled = "";
    // ZAP: Added logger
    private static final Logger LOGGER = LogManager.getLogger(Model.class);
    private List contextDataFactories = new ArrayList<>();
    private VariantFactory variantFactory = new VariantFactory();

    private boolean postInitialisation;

    public Model() {
        // make sure the variable here will not refer back to model itself.
        // DO it in init or respective getter.

        session = new Session(this);
        optionsParam = new OptionsParam();
    }

    /**
     * @return Returns the optionsParam.
     */
    public OptionsParam getOptionsParam() {
        if (optionsParam == null) {
            optionsParam = new OptionsParam();
        }
        return optionsParam;
    }

    /**
     * @param param The optionsParam to set.
     */
    public void setOptionsParam(OptionsParam param) {
        optionsParam = param;
    }

    /**
     * @return Returns the session.
     */
    public Session getSession() {
        if (session == null) {
            session = new Session(this);
        }
        return session;
    }

    /**
     * This method should typically only be called from the Control class
     *
     * @return Returns the session.
     */
    public Session newSession() {
        session = new Session(this);
        // Always start with one context
        session.saveContext(
                session.getNewContext(Constant.messages.getString("context.default.name")));
        return session;
    }

    /** This method should typically only be called from the Control class */
    public void openSession(String fileName)
            throws SQLException, SAXException, IOException, Exception {
        getSession().open(fileName);
    }

    public void openSession(String fileName, final SessionListener callback) {
        getSession().open(fileName, callback);
    }

    /** This method should typically only be called from the Control class */
    public void openSession(final File file, final SessionListener callback) {
        getSession().open(file, callback);
    }

    /** This method should typically only be called from the Control class */
    public void saveSession(final String fileName, final SessionListener callback) {
        getSession().save(fileName, callback);
    }

    /** This method should typically only be called from the Control class */
    public void saveSession(String fileName) throws Exception {
        getSession().save(fileName);
    }

    /**
     * Persists the properties (e.g. name, description) of the current session.
     *
     * 

Should be called only by "core" classes. * * @throws Exception if an error occurred while persisting the properties. * @since 2.7.0 */ public void persistSessionProperties() throws Exception { getSession().persistProperties(); } /** This method should typically only be called from the Control class */ public void snapshotSession(final String fileName, final SessionListener callback) { getSession().snapshot(fileName, callback); } /** This method should typically only be called from the Control class */ public void discardSession() { getSession().discard(); } /** This method should typically only be called from the Control class */ public void closeSession() { getSession().close(); } public void init(ControlOverrides overrides) throws SAXException, IOException, Exception { getOptionsParam().load(Constant.getInstance().FILE_CONFIG, overrides); if (overrides.isExperimentalDb()) { LOGGER.info("Using experimental database :/"); db = DbSQL.getSingleton().initDatabase(); } else { db = new ParosDatabase(); } db.setDatabaseOptions(getOptionsParam().getDatabaseParam()); createAndOpenUntitledDb(); HistoryReference.setTableHistory(getDb().getTableHistory()); HistoryReference.setTableTag(getDb().getTableTag()); HistoryReference.setTableAlert(getDb().getTableAlert()); } public static Model getSingleton() { if (model == null) { // ZAP: Changed to use the method createSingleton(). createSingleton(); } return model; } // ZAP: Added method. private static synchronized void createSingleton() { if (model == null) { model = new Model(); } } /** * Sets the given {@code Model} as the singleton. * *

Note: Not part of the public API. * * @param testModel the {@code Model} to test with. */ public static void setSingletonForTesting(Model testModel) { model = testModel; model.contextDataFactories = new ArrayList<>(); } /** * @return Returns the db. */ public Database getDb() { return db; } // TODO disable for non file based sessions public void moveSessionDb(String destFile) throws Exception { // always use copySession because moving file does not work in Debian, // and for Windows renaming file across different drives does not work. copySessionDb(currentDBNameUntitled, destFile); // getDb().close(); // // boolean result = false; // File fileIn1 = new File(currentDBNameUntitled + ".data"); // File fileIn2 = new File(currentDBNameUntitled + ".script"); // File fileIn3 = new File(currentDBNameUntitled + ".properties"); // File fileIn4 = new File(currentDBNameUntitled + ".backup"); // // File fileOut1 = new File(destFile + ".data"); // File fileOut2 = new File(destFile + ".script"); // File fileOut3 = new File(destFile + ".properties"); // File fileOut4 = new File(destFile + ".backup"); // // if (fileOut1.exists()) fileOut1.delete(); // if (fileOut2.exists()) fileOut2.delete(); // if (fileOut3.exists()) fileOut3.delete(); // if (fileOut4.exists()) fileOut4.delete(); // // result = fileIn1.renameTo(fileOut1); // result = fileIn2.renameTo(fileOut2); // result = fileIn3.renameTo(fileOut3); // if (fileIn4.exists()) { // result = fileIn4.renameTo(fileOut4); // } // // getDb().open(destFile); } // TODO disable for non file based sessions protected void copySessionDb(String currentFile, String destFile) throws Exception { // ZAP: Changed to call the method close(boolean, boolean). getDb().close(false, false); // copy session related files to the path specified FileCopier copier = new FileCopier(); // ZAP: Check if files exist. File fileIn1 = new File(currentFile + ".data"); if (fileIn1.exists()) { File fileOut1 = new File(destFile + ".data"); copier.copy(fileIn1, fileOut1); } File fileIn2 = new File(currentFile + ".script"); if (fileIn2.exists()) { File fileOut2 = new File(destFile + ".script"); copier.copy(fileIn2, fileOut2); } File fileIn3 = new File(currentFile + ".properties"); if (fileIn3.exists()) { File fileOut3 = new File(destFile + ".properties"); copier.copy(fileIn3, fileOut3); } File fileIn4 = new File(currentFile + ".backup"); if (fileIn4.exists()) { File fileOut4 = new File(destFile + ".backup"); copier.copy(fileIn4, fileOut4); } // ZAP: Handle the "lobs" file. File lobsFile = new File(currentFile + ".lobs"); if (lobsFile.exists()) { File newLobsFile = new File(destFile + ".lobs"); copier.copy(lobsFile, newLobsFile); } getDb().open(destFile); } // TODO disable for non file based sessions protected void snapshotSessionDb(String currentFile, String destFile) throws Exception { LOGGER.debug("snapshotSessionDb {} -> {}", currentFile, destFile); // ZAP: Changed to call the method close(boolean, boolean). getDb().close(false, false); // copy session related files to the path specified FileCopier copier = new FileCopier(); // ZAP: Check if files exist. File fileIn1 = new File(currentFile + ".data"); if (fileIn1.exists()) { File fileOut1 = new File(destFile + ".data"); copier.copy(fileIn1, fileOut1); } File fileIn2 = new File(currentFile + ".script"); if (fileIn2.exists()) { File fileOut2 = new File(destFile + ".script"); copier.copy(fileIn2, fileOut2); } File fileIn3 = new File(currentFile + ".properties"); if (fileIn3.exists()) { File fileOut3 = new File(destFile + ".properties"); copier.copy(fileIn3, fileOut3); } File fileIn4 = new File(currentFile + ".backup"); if (fileIn4.exists()) { File fileOut4 = new File(destFile + ".backup"); copier.copy(fileIn4, fileOut4); } // ZAP: Handle the "lobs" file. File lobsFile = new File(currentFile + ".lobs"); if (lobsFile.exists()) { File newLobsFile = new File(destFile + ".lobs"); copier.copy(lobsFile, newLobsFile); } if (currentFile.length() == 0) { LOGGER.debug("snapshotSessionDb using {} -> {}", currentDBNameUntitled, destFile); currentFile = currentDBNameUntitled; } getDb().open(currentFile); } /** This method should typically only be called from the Control class */ // TODO disable for non file based sessions public void createAndOpenUntitledDb() throws ClassNotFoundException, Exception { getDb().close(false, session.isCleanUpRequired()); // delete all untitled session db in "session" directory File dir = new File(getSession().getSessionFolder()); File[] listFile = dir.listFiles( new FilenameFilter() { @Override public boolean accept(File dir1, String fileName) { if (fileName.startsWith("untitled")) { return true; } return false; } }); for (int i = 0; i < listFile.length; i++) { if (!listFile[i].delete()) { // ZAP: Log failure to delete file LOGGER.error("Failed to delete file {}", listFile[i].getAbsolutePath()); } } // ZAP: Check if files exist. // copy and create new template db currentDBNameUntitled = DBNAME_UNTITLED + DBNAME_COPY; FileCopier copier = new FileCopier(); File fileIn = new File(Constant.getZapInstall(), DBNAME_TEMPLATE + ".data"); if (fileIn.exists()) { File fileOut = new File(currentDBNameUntitled + ".data"); if (fileOut.exists() && !fileOut.delete()) { // ZAP: Log failure to delete file LOGGER.error("Failed to delete file {}", fileOut.getAbsolutePath()); } copier.copy(fileIn, fileOut); } fileIn = new File(Constant.getZapInstall(), DBNAME_TEMPLATE + ".properties"); if (fileIn.exists()) { File fileOut = new File(currentDBNameUntitled + ".properties"); if (fileOut.exists() && !fileOut.delete()) { // ZAP: Log failure to delete file LOGGER.error("Failed to delete file {}", fileOut.getAbsolutePath()); } copier.copy(fileIn, fileOut); } fileIn = new File(Constant.getZapInstall(), DBNAME_TEMPLATE + ".script"); if (fileIn.exists()) { File fileOut = new File(currentDBNameUntitled + ".script"); if (fileOut.exists() && !fileOut.delete()) { // ZAP: Log failure to delete file LOGGER.error("Failed to delete file {}", fileOut.getAbsolutePath()); } copier.copy(fileIn, fileOut); } else { String fallbackResource = "/org/zaproxy/zap/resources/zapdb.script"; try (InputStream is = Model.class.getResourceAsStream(fallbackResource)) { if (is == null) { throw new FileNotFoundException( "Bundled resource not found: " + fallbackResource); } Files.copy(is, Paths.get(currentDBNameUntitled + ".script")); } } fileIn = new File(currentDBNameUntitled + ".backup"); if (fileIn.exists()) { if (!fileIn.delete()) { // ZAP: Log failure to delete file LOGGER.error("Failed to delete file {}", fileIn.getAbsolutePath()); } } // ZAP: Handle the "lobs" file. fileIn = new File(currentDBNameUntitled + ".lobs"); if (fileIn.exists()) { if (!fileIn.delete()) { LOGGER.error("Failed to delete file {}", fileIn.getAbsolutePath()); } } getDb().open(currentDBNameUntitled); DBNAME_COPY++; } @Deprecated public void addSessionListener(SessionListener listener) {} /** * Adds the given context data factory to the model. * * @param contextDataFactory the context data factory that will be added. * @throws IllegalArgumentException if the given parameter is {@code null}. * @see #removeContextDataFactory(ContextDataFactory) */ public void addContextDataFactory(ContextDataFactory contextDataFactory) { if (contextDataFactory == null) { throw new IllegalArgumentException("Parameter contextDataFactory must not be null."); } this.contextDataFactories.add(contextDataFactory); if (postInitialisation) { for (Context context : getSession().getContexts()) { contextDataFactory.loadContextData(getSession(), context); } } } /** * Removes the given context data factory from the model. * * @param contextDataFactory the context data factory that will be removed. * @throws IllegalArgumentException if the given parameter is {@code null}. * @since 2.5.0 * @see #addContextDataFactory(ContextDataFactory) */ public void removeContextDataFactory(ContextDataFactory contextDataFactory) { if (contextDataFactory == null) { throw new IllegalArgumentException("Parameter contextDataFactory must not be null."); } contextDataFactories.remove(contextDataFactory); } /** * Loads the given context, by calling all the {@link ContextDataFactory}ies. * * @param ctx the context to load. * @throws IllegalArgumentException (since 2.8.0) if the given context is {@code null}. * @see ContextDataFactory#loadContextData(Session, Context) * @since 2.0.0 */ public void loadContext(Context ctx) { validateContextNotNull(ctx); for (ContextDataFactory cdf : this.contextDataFactories) { cdf.loadContextData(getSession(), ctx); } } /** * Validates that the given context is not {@code null}, throwing an {@code * IllegalArgumentException} if it is. * * @param context the context to be validated. * @throws IllegalArgumentException if the context is {@code null}. */ private static void validateContextNotNull(Context context) { if (context == null) { throw new IllegalArgumentException("The context must not be null."); } } /** * Saves the given context, by calling all the {@link ContextDataFactory}ies. * * @param ctx the context to save. * @throws IllegalArgumentException (since 2.8.0) if the given context is {@code null}. * @since 2.0.0 * @see ContextDataFactory#persistContextData(Session, Context) */ public void saveContext(Context ctx) { validateContextNotNull(ctx); for (ContextDataFactory cdf : this.contextDataFactories) { cdf.persistContextData(getSession(), ctx); } } /** * Import a context from the given configuration * * @param ctx the context to import the context data to. * @param config the {@code Configuration} containing the context data. * @throws ConfigurationException if an error occurred while reading the context data from the * {@code Configuration}. * @throws IllegalArgumentException (since 2.8.0) if the given context or configuration is * {@code null}. * @since 2.4.0 */ public void importContext(Context ctx, Configuration config) throws ConfigurationException { validateContextNotNull(ctx); validateConfigNotNull(config); for (ContextDataFactory cdf : this.contextDataFactories) { cdf.importContextData(ctx, config); } } /** * Validates that the given configuration is not {@code null}, throwing an {@code * IllegalArgumentException} if it is. * * @param config the config to be validated. * @throws IllegalArgumentException if the config is {@code null}. */ private static void validateConfigNotNull(Configuration config) { if (config == null) { throw new IllegalArgumentException("The configuration must not be null."); } } /** * Export a context into the given configuration * * @param ctx the context to export. * @param config the {@code Configuration} where to export the context data. * @throws IllegalArgumentException (since 2.8.0) if the given context is {@code null}. * @since 2.4.0 */ public void exportContext(Context ctx, Configuration config) { validateContextNotNull(ctx); validateConfigNotNull(config); for (ContextDataFactory cdf : this.contextDataFactories) { cdf.exportContextData(ctx, config); } } /** * Notifies the model that the initialisation has been done. * *

Note: Should be called only by "core" code after the initialisation. * * @since 2.5.0 */ public void postInit() { postInitialisation = true; } /** * Returns the VariantFactory * * @return the VariantFactory * @since 2.10.0 */ public VariantFactory getVariantFactory() { return this.variantFactory; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy