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

Java tutorial

Introduction

Here is the source code for com.denimgroup.threadfix.service.remoteprovider.VeracodeRemoteProvider.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.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

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.DataFlowElement;
import com.denimgroup.threadfix.data.entities.Finding;
import com.denimgroup.threadfix.data.entities.RemoteProviderApplication;
import com.denimgroup.threadfix.data.entities.Scan;

public class VeracodeRemoteProvider extends RemoteProvider {

    private static final String GET_APP_BUILDS_URI = "https://analysiscenter.veracode.com/api/2.0/getappbuilds.do";
    private static final String GET_DETAILED_REPORT_URI = "https://analysiscenter.veracode.com/api/detailedreport.do";

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

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

        setChannelType(ChannelType.VERACODE);
    }

    @Override
    public List<Scan> getScans(RemoteProviderApplication remoteProviderApplication) {
        if (remoteProviderApplication == null || remoteProviderApplication.getApplicationChannel() == null) {
            log.error("Veracode getScan() called with invalid parameters. Returning null");
            return null;
        }

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

        // This block tries to get the latest build for the app and dies if it fails.
        InputStream appBuildsInputStream = getUrl(GET_APP_BUILDS_URI, username, password);
        String appName = remoteProviderApplication.getNativeId();
        VeracodeApplicationIdMapParser parser = new VeracodeApplicationIdMapParser();

        List<String> buildIds = null;

        if (appBuildsInputStream != null) {
            parse(appBuildsInputStream, parser);
            buildIds = parser.map.get(appName);
        }

        if (buildIds == null || buildIds.size() == 0) {
            log.warn("No build IDs were parsed.");
            return null; // we failed.
        } else {
            log.warn("Retrieved build IDs " + buildIds + " for application " + appName);
        }

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

        for (String buildId : buildIds) {
            if (buildId == null || buildId.trim().equals("")) {
                log.warn("Build ID was null or empty. This should never happen.");
                continue; // we failed.
            } else if (parser.dateMap.get(buildId).before(remoteProviderApplication.getLastImportTime())) {
                log.info("Build ID " + buildId + " was scanned before the most recent scan in ThreadFix.");
                continue;
            }

            log.warn("Importing scan for build ID " + buildId + " and application " + appName);

            // This block tries to parse the scan corresponding to the build.
            inputStream = getUrl(GET_DETAILED_REPORT_URI + "?build_id=" + buildId, username, password);

            if (inputStream == null) {
                log.warn("Received a bad response from Veracode servers, returning null.");
                continue;
            }

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

            if (resultScan == null) {
                log.error("No scan was parsed, something is broken.");
                continue;
            }

            resultScan.setImportTime(parser.dateMap.get(buildId));
            resultScan.setApplicationChannel(remoteProviderApplication.getApplicationChannel());

            log.info("Veracode scan (Build ID " + buildId + ") was successfully parsed.");

            scans.add(resultScan);
        }

        return scans;
    }

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

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

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

        InputStream stream = null;

        stream = getUrl(GET_APP_BUILDS_URI, username, password);

        if (stream == null) {
            log.warn("Got a bad response from Veracode. Check your username and password.");
            return null;
        }

        VeracodeApplicationBuildsParser parser = new VeracodeApplicationBuildsParser();

        parse(stream, parser);

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

        return parser.list;
    }

    public InputStream getUrl(String urlString, String username, String password) {
        URL url = null;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }

        HttpsURLConnection m_connect;
        try {
            m_connect = (HttpsURLConnection) url.openConnection();

            setupAuthorization(m_connect, username, password);

            InputStream is = m_connect.getInputStream();

            return is;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setupAuthorization(HttpsURLConnection connection, String username, String password) {
        String login = username + ":" + password;
        String encodedLogin = new String(Base64.encodeBase64(login.getBytes()));
        //String encodedLogin = Base64.encodeBase64String(login.getBytes());
        connection.setRequestProperty("Authorization", "Basic " + encodedLogin);
    }

    public class VeracodeApplicationBuildsParser extends DefaultHandler {

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

        public void startElement(String uri, String name, String qName, Attributes atts) throws SAXException {
            if (qName.equals("application")) {
                RemoteProviderApplication remoteProviderApplication = new RemoteProviderApplication();
                remoteProviderApplication.setNativeId(atts.getValue("app_name"));
                remoteProviderApplication.setRemoteProviderType(remoteProviderType);
                list.add(remoteProviderApplication);
            }
        }
    }

    public class VeracodeApplicationIdMapParser extends DefaultHandler {

        public Map<String, List<String>> map = new HashMap<String, List<String>>();
        public Map<String, Calendar> dateMap = new HashMap<String, Calendar>();

        private String currentAppName = null;
        private String currentBuildId = null;

        public void startElement(String uri, String name, String qName, Attributes atts) throws SAXException {
            if (qName.equals("application")) {
                currentAppName = atts.getValue("app_name");
                map.put(currentAppName, new ArrayList<String>());
            } else if (currentAppName != null && qName.equals("build")) {
                currentBuildId = atts.getValue("build_id");
                map.get(currentAppName).add(currentBuildId);
            } else if (currentAppName != null && currentBuildId != null && qName.equals("analysis_unit")) {

                String dateString = atts.getValue("published_date");
                if (dateString != null && dateString.length() > 5)
                    dateString = dateString.substring(0, dateString.length() - 5);

                Calendar calendar = getCalendarFromString("yyyy-MM-DD'T'HH:mm:ss", dateString);
                dateMap.put(currentBuildId, calendar);
            }
        }
    }

    public class VeracodeSAXParser extends DefaultHandler {

        private boolean inStaticFlaws = true;

        private Finding lastFinding = null;
        private boolean mitigationProposed = false;

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

        public void startElement(String uri, String name, String qName, Attributes atts) {
            if ("detailedreport".equals(qName)) {
                date = getCalendarFromString("yyyy-MM-dd kk:mm:ss", atts.getValue("last_update_time"));
                if (date == null)
                    date = getCalendarFromString("yyyy-MM-dd kk:mm:ss", atts.getValue("generation_date"));
            }

            if ("dynamicflaws".equals(qName)) {
                inStaticFlaws = false;
            }

            // TODO look through more Veracode scans and see if the inputvector component is the parameter.
            if ("flaw".equals(qName)) {
                if ("Fixed".equals(atts.getValue("remediation_status")))
                    return;

                String url = null;
                if (atts.getValue("url") != null)
                    url = atts.getValue("url");
                else if (atts.getValue("location") != null)
                    url = atts.getValue("location");

                Finding finding = constructFinding(url, atts.getValue("vuln_parameter"), atts.getValue("cweid"),
                        atts.getValue("severity"));
                if (finding != null) {
                    finding.setNativeId(atts.getValue("issueid"));

                    // TODO revise this method of deciding whether the finding is static.                
                    finding.setIsStatic(inStaticFlaws);
                    if (atts.getValue("sourcefile") != null && atts.getValue("sourcefilepath") != null) {
                        String sourceFileLocation = atts.getValue("sourcefilepath") + atts.getValue("sourcefile");
                        finding.setSourceFileLocation(sourceFileLocation);
                        finding.getSurfaceLocation().setPath(sourceFileLocation);
                        if (atts.getValue("line") != null) {
                            DataFlowElement dataFlowElement = new DataFlowElement();
                            dataFlowElement.setFinding(finding);
                            try {
                                dataFlowElement.setLineNumber(Integer.valueOf(atts.getValue("line")));
                            } catch (NumberFormatException e) {
                                log.error(
                                        "Non-numeric value '" + atts.getValue("line")
                                                + "' found in Veracode results when trying to parse line number.",
                                        e);
                            }
                            dataFlowElement.setSourceFileName(sourceFileLocation);
                            finding.setDataFlowElements(new ArrayList<DataFlowElement>());
                            finding.getDataFlowElements().add(dataFlowElement);
                        }
                    }
                    lastFinding = finding;
                    mitigationProposed = false;
                    saxFindingList.add(finding);
                }
            }

            if (mitigationProposed && "mitigation".equals(qName) && atts.getValue("action") != null
                    && atts.getValue("action").equals("Mitigation Accepted")) {
                mitigationProposed = false;
                lastFinding.setMarkedFalsePositive(true);
                log.info("The false positive mitigation was accepted.");
            }

            if ("mitigation".equals(qName) && atts.getValue("action") != null
                    && atts.getValue("action").equals("Mitigated as Potential False Positive")) {
                mitigationProposed = true;
                log.info("Found a Finding with false positive mitigation proposed.");
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equals("dynamicflaws")) {
                if ("dynamicflaws".equals(qName)) {
                    inStaticFlaws = true;
                }
            }
        }
    }
}