com.denimgroup.threadfix.service.remoteprovider.QualysRemoteProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.denimgroup.threadfix.service.remoteprovider.QualysRemoteProvider.java

Source

////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2013 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
//
//     Software distributed under the License is distributed on an "AS IS"
//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//     License for the specific language governing rights and limitations
//     under the License.
//
//     The Original Code is ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.service.remoteprovider;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import com.denimgroup.threadfix.data.dao.ChannelSeverityDao;
import com.denimgroup.threadfix.data.dao.ChannelTypeDao;
import com.denimgroup.threadfix.data.dao.ChannelVulnerabilityDao;
import com.denimgroup.threadfix.data.entities.ChannelType;
import com.denimgroup.threadfix.data.entities.Finding;
import com.denimgroup.threadfix.data.entities.RemoteProviderApplication;
import com.denimgroup.threadfix.data.entities.RemoteProviderType;
import com.denimgroup.threadfix.data.entities.Scan;
import com.denimgroup.threadfix.service.channel.HandlerWithBuilder;

/**
 * TODO use POST data to pre-filter web requests
 * @author mcollins
 *
 */
public class QualysRemoteProvider extends RemoteProvider {

    private String username = null;
    private String password = null;

    private static final Map<String, String> SEVERITIES_MAP = new HashMap<String, String>();
    static {
        SEVERITIES_MAP.put("150000", "5");
        SEVERITIES_MAP.put("150001", "5");
        SEVERITIES_MAP.put("150002", "5");
        SEVERITIES_MAP.put("150003", "5");
        SEVERITIES_MAP.put("150004", "2");
        SEVERITIES_MAP.put("150005", "2");
        SEVERITIES_MAP.put("150006", "1");
        SEVERITIES_MAP.put("150007", "1");
        SEVERITIES_MAP.put("150008", "1");
        SEVERITIES_MAP.put("150009", "1");
        SEVERITIES_MAP.put("150010", "1");
        SEVERITIES_MAP.put("150011", "3");
        SEVERITIES_MAP.put("150012", "5");
        SEVERITIES_MAP.put("150013", "5");
        SEVERITIES_MAP.put("150014", "1");
        SEVERITIES_MAP.put("150015", "1");
        SEVERITIES_MAP.put("150016", "2");
        SEVERITIES_MAP.put("150017", "1");
        SEVERITIES_MAP.put("150018", "2");
        SEVERITIES_MAP.put("150019", "2");
        SEVERITIES_MAP.put("150020", "1");
        SEVERITIES_MAP.put("150021", "1");
        SEVERITIES_MAP.put("150022", "3");
        SEVERITIES_MAP.put("150023", "2");
        SEVERITIES_MAP.put("150024", "1");
        SEVERITIES_MAP.put("150025", "1");
        SEVERITIES_MAP.put("150026", "1");
        SEVERITIES_MAP.put("150028", "1");
        SEVERITIES_MAP.put("150029", "1");
        SEVERITIES_MAP.put("150030", "1");
        SEVERITIES_MAP.put("150032", "3");
        SEVERITIES_MAP.put("150033", "3");
        SEVERITIES_MAP.put("150034", "3");
        SEVERITIES_MAP.put("150035", "1");
        SEVERITIES_MAP.put("150036", "1");
        SEVERITIES_MAP.put("150037", "1");
        SEVERITIES_MAP.put("150038", "1");
        SEVERITIES_MAP.put("150039", "1");
        SEVERITIES_MAP.put("150040", "1");
        SEVERITIES_MAP.put("150041", "1");
        SEVERITIES_MAP.put("150042", "3");
        SEVERITIES_MAP.put("150043", "1");
        SEVERITIES_MAP.put("150044", "3");
        SEVERITIES_MAP.put("150045", "3");
        SEVERITIES_MAP.put("150046", "5");
        SEVERITIES_MAP.put("150047", "5");
        SEVERITIES_MAP.put("150048", "5");
        SEVERITIES_MAP.put("150049", "4");
        SEVERITIES_MAP.put("150051", "3");
        SEVERITIES_MAP.put("150054", "1");
        SEVERITIES_MAP.put("150055", "5");
        SEVERITIES_MAP.put("150057", "5");
        SEVERITIES_MAP.put("150058", "1");
        SEVERITIES_MAP.put("150060", "5");
        SEVERITIES_MAP.put("150061", "1");
        SEVERITIES_MAP.put("150062", "5");
        SEVERITIES_MAP.put("150063", "5");
        SEVERITIES_MAP.put("150064", "1");
        SEVERITIES_MAP.put("150065", "2");
        SEVERITIES_MAP.put("150066", "1");
        SEVERITIES_MAP.put("150067", "3");
        SEVERITIES_MAP.put("150071", "3");
        SEVERITIES_MAP.put("150076", "4");
        SEVERITIES_MAP.put("150077", "1");
        SEVERITIES_MAP.put("150078", "1");
        SEVERITIES_MAP.put("150079", "3");
        SEVERITIES_MAP.put("150081", "1");
        SEVERITIES_MAP.put("150082", "1");
        SEVERITIES_MAP.put("150083", "1");
        SEVERITIES_MAP.put("150084", "1");
        SEVERITIES_MAP.put("150085", "3");
        SEVERITIES_MAP.put("150086", "3");
        SEVERITIES_MAP.put("150087", "1");
        SEVERITIES_MAP.put("150088", "1");
        SEVERITIES_MAP.put("150089", "3");
        SEVERITIES_MAP.put("150090", "5");
        SEVERITIES_MAP.put("150092", "5");
        SEVERITIES_MAP.put("150093", "5");
        SEVERITIES_MAP.put("150094", "1");
        SEVERITIES_MAP.put("150095", "1");
    }

    @Autowired
    public QualysRemoteProvider(ChannelTypeDao channelTypeDao, ChannelVulnerabilityDao channelVulnerabilityDao,
            ChannelSeverityDao channelSeverityDao) {
        this.channelVulnerabilityDao = channelVulnerabilityDao;
        this.channelTypeDao = channelTypeDao;
        this.channelSeverityDao = channelSeverityDao;

        setChannelType(ChannelType.QUALYSGUARD_WAS);
    }

    @Override
    public List<Scan> getScans(RemoteProviderApplication remoteProviderApplication) {
        if (remoteProviderApplication == null || remoteProviderApplication.getRemoteProviderType() == null) {
            log.error("Null input to Qualys getScan(), returning null.");
            return null;
        }

        password = remoteProviderApplication.getRemoteProviderType().getPassword();
        username = remoteProviderApplication.getRemoteProviderType().getUsername();

        List<String> scanIds = mostRecentScanForApp(remoteProviderApplication);

        if (scanIds == null || scanIds.size() == 0) {
            log.warn("No valid scans were found.");
            return null;
        }

        List<Scan> scanList = new ArrayList<Scan>();

        for (String scanId : scanIds) {
            inputStream = httpGet(getScanUrl(remoteProviderApplication.getRemoteProviderType()) + scanId);

            if (inputStream == null) {
                log.warn(
                        "Got a bad response from Qualys servers for scan ID " + scanId + ". Trying the next scan.");
                continue;
            }

            QualysWASSAXParser scanParser = new QualysWASSAXParser();
            Scan resultScan = parseSAXInput(scanParser);

            if (resultScan != null) {
                log.info("The Qualys scan import for scan ID " + scanId + " was successful.");
                resultScan.setApplicationChannel(remoteProviderApplication.getApplicationChannel());
                scanList.add(resultScan);
            } else {
                log.warn("The Qualys scan import for scan ID " + scanId + " failed!!");
            }
        }

        return scanList;
    }

    @Override
    public List<RemoteProviderApplication> fetchApplications() {
        if (remoteProviderType == null || remoteProviderType.getUsername() == null
                || remoteProviderType.getPassword() == null) {
            log.error("Insufficient credentials given to Qualys fetchApplications().");
            return null;
        }

        log.info("Fetching Qualys applications.");

        password = remoteProviderType.getPassword();
        username = remoteProviderType.getUsername();

        InputStream stream = null;

        // POST with no parameters
        // TODO include filters
        stream = httpPost(getAppsUrl(remoteProviderType), new String[] {}, new String[] {});

        if (stream == null) {
            log.warn("Response from Qualys servers was null, check your credentials.");
            return null;
        }

        QualysAppsParser parser = new QualysAppsParser();

        parse(stream, parser);

        if (parser.list != null && parser.list.size() > 0) {
            log.info("Number of Qualys applications: " + parser.list.size());
        } else {
            log.warn("No Qualys applications were found. Check your configuration.");
        }

        return parser.list;
    }

    public List<String> mostRecentScanForApp(RemoteProviderApplication app) {
        if (app == null || app.getNativeId() == null) {
            return null;
        }

        // POST with no parameters
        // TODO include filters
        InputStream stream = httpPost(getScansForAppUrl(app.getRemoteProviderType()), new String[] {},
                new String[] {});

        if (stream == null) {
            return null;
        }

        QualysScansForAppParser parser = new QualysScansForAppParser();
        parse(stream, parser);

        List<String> scanIds = new ArrayList<String>();

        // This should be replaced with the filtered code
        for (Map<String, String> map : parser.list) {
            Calendar mapDate = null;

            if (app.getNativeId().equals(map.get("webAppName")) && map.get("date") != null) {
                mapDate = getCalendarFromString("yyyy-MM-DD'T'HH:mm:ss'Z'", map.get("date"));
                if (app.getLastImportTime() == null || mapDate.after(app.getLastImportTime())) {
                    scanIds.add(map.get("id"));
                }
            }
        }

        log.info("Returning scan IDs " + scanIds + " for application " + app.getNativeId());

        return scanIds;
    }

    public QualysRemoteProvider() {

    }

    private String getScansForAppUrl(RemoteProviderType type) {
        return "https://qualysapi.qualys." + getLocation(type.getIsEuropean()) + "/qps/rest/3.0/search/was/wasscan";
    }

    private String getScanUrl(RemoteProviderType type) {
        return "https://qualysapi.qualys." + getLocation(type.getIsEuropean())
                + "/qps/rest/3.0/download/was/wasscan/";
    }

    private String getAppsUrl(RemoteProviderType type) {
        return "https://qualysapi.qualys." + getLocation(type.getIsEuropean()) + "/qps/rest/3.0/search/was/webapp";
    }

    private String getLocation(boolean isEuropean) {
        return isEuropean ? "eu" : "com";
    }

    // UTILITIES

    private InputStream httpPost(String request, String[] paramNames, String[] paramVals) {

        PostMethod post = new PostMethod(request);

        post.setRequestHeader("Content-type", "text/xml; charset=UTF-8");

        if (username != null && password != null) {
            String login = username + ":" + password;
            String encodedLogin = new String(Base64.encodeBase64(login.getBytes()));

            post.setRequestHeader("Authorization", "Basic " + encodedLogin);
        }

        try {
            for (int i = 0; i < paramNames.length; i++) {
                post.addParameter(paramNames[i], paramVals[i]);
            }

            HttpClient client = new HttpClient();

            int status = client.executeMethod(post);

            if (status != 200) {
                log.warn("Status was not 200.");
                log.warn("Status : " + status);
            }

            InputStream responseStream = post.getResponseBodyAsStream();

            if (responseStream != null) {
                return responseStream;
            }

        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (HttpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        log.warn("There was an error and the POST request was not finished.");
        return null;
    }

    private InputStream httpGet(String urlStr) {
        GetMethod get = new GetMethod(urlStr);

        get.setRequestHeader("Content-type", "text/xml; charset=UTF-8");

        if (username != null && password != null) {
            String login = username + ":" + password;
            String encodedLogin = new String(Base64.encodeBase64(login.getBytes()));

            get.setRequestHeader("Authorization", "Basic " + encodedLogin);
        }

        HttpClient client = new HttpClient();
        try {
            int status = client.executeMethod(get);
            if (status != 200) {
                log.warn("Status was not 200.");
                log.warn("Status : " + status);
            }

            InputStream responseStream = get.getResponseBodyAsStream();

            if (responseStream != null) {
                return responseStream;
            }
        } catch (HttpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    // PARSER CLASSES

    private class QualysAppsParser extends HandlerWithBuilder {

        public List<RemoteProviderApplication> list = new ArrayList<RemoteProviderApplication>();

        private boolean getName = false;

        public void startElement(String uri, String name, String qName, Attributes atts) throws SAXException {
            if (qName.equals("name")) {
                getName = true;
            }
        }

        public void endElement(String uri, String name, String qName) {
            if (getName) {
                String tempNameString = getBuilderText();

                RemoteProviderApplication remoteProviderApplication = new RemoteProviderApplication();
                remoteProviderApplication.setNativeId(tempNameString);
                remoteProviderApplication.setRemoteProviderType(remoteProviderType);
                list.add(remoteProviderApplication);

                getName = false;
            }
        }

        public void characters(char ch[], int start, int length) {
            if (getName) {
                addTextToBuilder(ch, start, length);
            }
        }
    }

    private class QualysScansForAppParser extends HandlerWithBuilder {

        public List<Map<String, String>> list = new ArrayList<Map<String, String>>();

        private boolean inWebApp = false;
        private boolean getName = false;
        private String webAppName = "";

        private boolean getId = false;
        private String currentId = null;

        private boolean getStatus = false;
        private String currentStatus = null;

        private boolean getDate = false;
        private String currentDate = null;

        public void startElement(String uri, String name, String qName, Attributes atts) throws SAXException {

            if (qName.equals("webApp")) {
                inWebApp = true;
            } else if (currentId == null && qName.equals("id")) {
                getId = true;
            } else if (inWebApp && qName.equals("name")) {
                getName = true;
                inWebApp = false;
            } else if (qName.equals("status")) {
                getStatus = true;
            } else if (qName.equals("launchedDate")) {
                getDate = true;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (getId) {
                currentId = getBuilderText();
                getId = false;
            } else if (getStatus) {
                currentStatus = getBuilderText();
                getStatus = false;
            } else if (getDate) {
                currentDate = getBuilderText();
                getDate = false;
            } else if (getName) {
                String toAdd = getBuilderText();

                webAppName = toAdd;

                getName = false;
            }

            if (qName.equals("WasScan")) {
                Map<String, String> map = new HashMap<String, String>();
                map.put("id", currentId);
                map.put("status", currentStatus);
                map.put("date", currentDate);
                map.put("webAppName", webAppName);

                list.add(map);

                currentStatus = null;
                currentId = null;
                currentDate = null;
                webAppName = null;
            }
        }

        public void characters(char ch[], int start, int length) {
            if (getId || getStatus || getDate || getName) {
                addTextToBuilder(ch, start, length);
            }
        }
    }

    private class QualysWASSAXParser extends HandlerWithBuilder {
        private Boolean getDate = false;
        private Boolean getUri = false;
        private Boolean getParameter = false;
        private Boolean getChannelVulnName = false;

        private String currentChannelVulnCode = null;
        private String currentPath = null;
        private String currentParameter = null;
        private String currentSeverityCode = null;

        public void add(Finding finding) {
            if (finding != null) {
                finding.setNativeId(getNativeId(finding));
                finding.setIsStatic(false);
                saxFindingList.add(finding);
            }
        }

        ////////////////////////////////////////////////////////////////////
        // Event handlers.
        ////////////////////////////////////////////////////////////////////

        public void startElement(String uri, String name, String qName, Attributes atts) {
            if ("launchedDate".equals(qName)) {
                getDate = true;
            } else if ("uri".equals(qName)) {
                getUri = true;
            } else if ("qid".equals(qName)) {
                getChannelVulnName = true;
            } else if ("param".equals(qName)) {
                getParameter = true;
            } else if ("instances".equals(qName)) {
                currentSeverityCode = SEVERITIES_MAP.get(currentChannelVulnCode);

                Finding finding = constructFinding(currentPath, currentParameter, currentChannelVulnCode,
                        currentSeverityCode);
                add(finding);

                currentParameter = null;
                currentPath = null;
                getParameter = false;
            }
        }

        public void endElement(String uri, String name, String qName) {
            if (getDate) {
                String tempDateString = getBuilderText();

                if (tempDateString != null && !tempDateString.trim().isEmpty()) {
                    date = getCalendarFromString("yyyy-MM-DD'T'HH:mm:ss'Z'", tempDateString);
                }
                getDate = false;
            } else if (getUri) {
                currentPath = getBuilderText();
                getUri = false;
            } else if (getChannelVulnName) {
                currentChannelVulnCode = getBuilderText();
                getChannelVulnName = false;
            } else if (getParameter) {
                currentParameter = getBuilderText();
                getParameter = false;
            }
        }

        public void characters(char ch[], int start, int length) {
            if (getDate || getUri || getChannelVulnName || getParameter) {
                addTextToBuilder(ch, start, length);
            }
        }
    }
}