gov.nist.appvet.toolmgr.ToolServiceAdapter.java Source code

Java tutorial

Introduction

Here is the source code for gov.nist.appvet.toolmgr.ToolServiceAdapter.java

Source

/* This software was developed by employees of the National Institute of
 * Standards and Technology (NIST), an agency of the Federal Government.
 * Pursuant to title 15 United States Code Section 105, works of NIST
 * employees are not subject to copyright protection in the United States
 * and are considered to be in the public domain.  As a result, a formal
 * license is not needed to use the software.
 * 
 * This software is provided by NIST as a service and is expressly
 * provided "AS IS".  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
 * AND DATA ACCURACY.  NIST does not warrant or make any representations
 * regarding the use of the software or the results thereof including, but
 * not limited to, the correctness, accuracy, reliability or usefulness of
 * the software.
 * 
 * Permission to use this software is contingent upon your acceptance
 * of the terms of this agreement.
 */
package gov.nist.appvet.toolmgr;

import gov.nist.appvet.properties.AppVetProperties;
import gov.nist.appvet.servlet.shared.SSLWrapper;
import gov.nist.appvet.shared.ErrorMessage;
import gov.nist.appvet.shared.Logger;
import gov.nist.appvet.shared.ReportFileType;
import gov.nist.appvet.shared.app.AppInfo;
import gov.nist.appvet.shared.status.ToolStatus;
import gov.nist.appvet.shared.status.ToolStatusManager;
import gov.nist.appvet.xml.XmlUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.PDFTextStripper;

public class ToolServiceAdapter implements Runnable {

    private String appvetResultHeaderName = null;
    public boolean reportPayload = false;
    private static final Logger log = AppVetProperties.log;
    public Thread thread = null;
    public AppInfo appInfo = null;
    public String name = null; // Display Name (e.g., Androwarn (Maaaaz))
    public String id = null; // ID used as a database table column name
    public String vendorName = null;
    public Protocol protocol = null;
    public String webSite = null;
    public String URL = null;
    public String method = null;
    public ArrayList<String> formParameterNames = null;
    public ArrayList<String> formParameterValues = null;
    public ReportFileType reportFileType = null;
    public String reportName = null;
    public ArrayList<String> securityPassPhrases = null;
    public ArrayList<String> securityWarningPhrases = null;
    public ArrayList<String> securityFailPhrases = null;
    public ArrayList<String> errorPhrases = null;

    public enum Protocol {

        SYNCHRONOUS, ASYNCHRONOUS, PUSH, INTERNAL;

        public static Protocol getProtocol(String protocolName) {
            if (protocolName != null) {
                for (final Protocol m : Protocol.values()) {
                    if (protocolName.equalsIgnoreCase(m.name())) {
                        return m;
                    }
                }
            }
            return null;
        }

    };

    public static ToolServiceAdapter getById(String toolId) {
        for (int i = 0; i < AppVetProperties.availableTools.size(); i++) {
            final ToolServiceAdapter adapter = AppVetProperties.availableTools.get(i);
            if (adapter.id.equals(toolId)) {
                return adapter;
            }
        }
        log.error("Tool id '" + toolId + "' does not exist!");
        return null;
    }

    public static String getHtmlReportString(String reportPath, AppInfo appInfo) {
        byte[] encoded = null;
        try {
            encoded = Files.readAllBytes(Paths.get(reportPath));
            return Charset.defaultCharset().decode(ByteBuffer.wrap(encoded)).toString();
        } catch (final IOException e) {
            appInfo.log.error(e.getMessage());
            return null;
        } finally {
            encoded = null;
        }
    }

    public static String getPdfReportString(String reportPath, AppInfo appInfo) {
        File file = new File(reportPath);
        PDDocument pddDocument = null;
        PDFTextStripper textStripper = null;
        try {
            pddDocument = PDDocument.load(file);
            textStripper = new PDFTextStripper();
            textStripper.setStartPage(1);
            textStripper.setEndPage(1);
            final String report = textStripper.getText(pddDocument);
            return report;
        } catch (final IOException e) {
            appInfo.log.error(e.getMessage());
            return null;
        } finally {
            if (pddDocument != null) {
                try {
                    pddDocument.close();
                    pddDocument = null;
                } catch (IOException e) {
                    appInfo.log.error(e.getMessage());
                }
            }
            textStripper = null;
            file = null;
        }
    }

    public static String getTextReportString(String reportPath, AppInfo appInfo) {
        byte[] encoded = null;
        try {
            encoded = Files.readAllBytes(Paths.get(reportPath));
            return Charset.defaultCharset().decode(ByteBuffer.wrap(encoded)).toString();
        } catch (final IOException e) {
            appInfo.log.error(e.getMessage());
            return null;
        } finally {
            encoded = null;
        }
    }

    public ToolServiceAdapter(File configFile) {
        if (!configFile.exists()) {
            log.error("Test service config file " + configFile.getName() + " does not exist.");
            return;
        }
        XmlUtil xml = new XmlUtil(configFile);
        final String configFileName = configFile.getName();

        // Tool configuration
        name = xml.getXPathValue("/ToolServiceAdapter/Description/Name");
        checkNullString(configFileName, "name", name);
        id = xml.getXPathValue("/ToolServiceAdapter/Description/Id");
        checkNullString(configFileName, "id", id);
        vendorName = xml.getXPathValue("/ToolServiceAdapter/Description/VendorName");
        webSite = xml.getXPathValue("/ToolServiceAdapter/Description/VendorWebsite");
        // Report configuration  
        final String reportFileTypeString = xml.getXPathValue("/ToolServiceAdapter/Description/ReportFile");
        checkNullString(configFileName, "reportFileTypeString", reportFileTypeString);
        reportFileType = ReportFileType.getFileType(reportFileTypeString);
        reportName = getReportName();

        // Protocol config
        final String protocolName = xml.getXPathValue("/ToolServiceAdapter/Protocol/Type");
        checkNullString(configFileName, "protocolName", protocolName);
        protocol = Protocol.getProtocol(protocolName);
        String protocolXPath = "/ToolServiceAdapter/Protocol";
        switch (protocol) {
        case SYNCHRONOUS:
            protocolXPath += "/Synchronous";
            checkNullString(configFileName, "protocolXPath", protocolXPath);
            // HTTPRequestType
            URL = xml.getXPathValue(protocolXPath + "/Request/URL");
            checkNullString(configFileName, "URL", URL);
            method = xml.getXPathValue(protocolXPath + "/Request/Method");
            checkNullString(configFileName, "method", method);
            formParameterNames = xml.getXPathValues(protocolXPath + "/Request/Parameter/Name");
            checkNullArrayList(configFileName, "formParameterNames", formParameterNames);
            formParameterValues = xml.getXPathValues(protocolXPath + "/Request/Parameter/Value");
            checkNullArrayList(configFileName, "formParameterValues", formParameterValues);
            // HTTPResponseType
            appvetResultHeaderName = xml.getXPathValue(protocolXPath + "/Response/AppVetRiskHeaderName");
            String reportPayloadString = xml.getXPathValue(protocolXPath + "/Response/ReportPayload");
            reportPayload = new Boolean(reportPayloadString).booleanValue();
            break;
        case ASYNCHRONOUS:
            protocolXPath += "/Asynchronous";
            checkNullString(configFileName, "protocolXPath", protocolXPath);
            // HTTPRequestType
            URL = xml.getXPathValue(protocolXPath + "/Request/URL");
            checkNullString(configFileName, "URL", URL);
            method = xml.getXPathValue(protocolXPath + "/Request/Method");
            checkNullString(configFileName, "method", method);
            formParameterNames = xml.getXPathValues(protocolXPath + "/Request/Parameter/Name");
            checkNullArrayList(configFileName, "formParameterNames", formParameterNames);
            formParameterValues = xml.getXPathValues(protocolXPath + "/Request/Parameter/Value");
            checkNullArrayList(configFileName, "formParameterValues", formParameterValues);
            // HTTPResponseType (not used for asynchronous services)                  
            break;
        case PUSH:
            protocolXPath += "/Push";
            break;
        case INTERNAL:
            protocolXPath += "/Internal";
            break;
        }

        xml = null;
    }

    public void checkNullArrayList(String fileName, String parameter, ArrayList<String> value) {
        if ((value == null) || value.isEmpty()) {
            log.error("Required parameter '" + parameter + "' in file " + fileName + " is null or empty.");
        }
    }

    public void checkNullString(String fileName, String parameter, String value) {
        if ((value == null) || value.isEmpty()) {
            log.error("Required parameter '" + parameter + "' in file " + fileName + " is null or empty.");
        }
    }

    public void cleanUp(AppInfo appInfo, boolean sendMobilizeReport) {
        if ((protocol == Protocol.PUSH) || (protocol == Protocol.INTERNAL)) {
            // PUSH adapters should not have a thread to clean up.
            return;
        }
        if (thread.isAlive()) {
            appInfo.log.debug("Thread for " + name + " is still alive.  Interrupting...");
            thread.interrupt();
            appInfo.log.error(ErrorMessage.TOOL_TIMEOUT_ERROR.getDescription());
            ToolStatusManager.setToolStatus(appInfo.appId, this.id, ToolStatus.ERROR);
        }
        thread = null;
    }

    public MultipartEntity getMultipartEntity() {
        final MultipartEntity entity = new MultipartEntity();
        File apkFile = null;
        FileBody fileBody = null;
        try {
            String fileUploadParamName = null;
            // Add parameter name-value pairs
            if (formParameterNames != null) {
                for (int i = 0; i < formParameterNames.size(); i++) {
                    final String paramName = formParameterNames.get(i);
                    String paramValue = formParameterValues.get(i);
                    if (paramValue.equals("APP_FILE")) {
                        fileUploadParamName = paramName;
                    } else {
                        if (paramValue.equals("APPVET_DEFINED")) {
                            if (paramName.equals("appid")) {
                                appInfo.log.debug("Found " + paramName + " = " + "'APPVET_DEFINED' for tool '" + id
                                        + "'. Setting to appid = '" + appInfo.appId + "'");
                                paramValue = appInfo.appId;
                            } else {
                                appInfo.log.error("Found " + paramName + " = " + "'APPVET_DEFINED' for tool '" + id
                                        + "' but no actual value is set by AppVet. Aborting.");
                                return null;
                            }
                        }
                        if ((paramName == null) || paramName.isEmpty()) {
                            appInfo.log.warn("Param name is null or empty " + "for tool '" + name + "'");
                        } else if ((paramValue == null) || paramValue.isEmpty()) {
                            appInfo.log.warn("Param value is null or empty " + "for tool '" + name + "'");
                        }
                        StringBody partValue = new StringBody(paramValue, Charset.forName("UTF-8"));
                        entity.addPart(paramName, partValue);
                        partValue = null;
                    }
                }
            }
            final String apkFilePath = appInfo.getIdPath() + "/" + appInfo.fileName;
            appInfo.log.debug("Sending file: " + apkFilePath);
            apkFile = new File(apkFilePath);
            fileBody = new FileBody(apkFile);
            entity.addPart(fileUploadParamName, fileBody);
            return entity;
        } catch (final UnsupportedEncodingException e) {
            appInfo.log.error(e.getMessage());
            return null;
        }
    }

    private String getReportName() {
        final String reportSuffix = "_security_report";
        switch (reportFileType) {
        case TXT:
            return id + reportSuffix + ".txt";
        case HTML:
            return id + reportSuffix + ".html";
        case PDF:
            return id + reportSuffix + ".pdf";
        case RTF:
            return id + reportSuffix + ".rtf";
        case XML:
            return id + reportSuffix + ".xml";
        default:
            return null;
        }
    }

    public Thread getThread() {
        thread = new Thread(this);
        return thread;
    }

    @Override
    public void run() {
        if ((protocol == Protocol.PUSH) || (protocol == Protocol.INTERNAL)) {
            // PUSH/INTERNAL adapters do not send requests to a service.
            return;
        }
        if (appInfo == null) {
            log.error("AppInfo object is null in tool adapter. " + "Aborting processing app.");
            return;
        }
        try {
            Date startDate = new Date();
            final long startTime = startDate.getTime();
            startDate = null;
            ToolStatusManager.setToolStatus(appInfo.appId, this.id, ToolStatus.SUBMITTED);
            HttpParams httpParameters = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(httpParameters, AppVetProperties.CONNECTION_TIMEOUT);
            HttpConnectionParams.setSoTimeout(httpParameters, AppVetProperties.SO_TIMEOUT);
            HttpClient httpclient = new DefaultHttpClient(httpParameters);
            httpclient = SSLWrapper.wrapClient(httpclient);
            MultipartEntity entity = getMultipartEntity();
            if (entity == null) {
                appInfo.log.error("MultipartEntity is null. Aborting " + name);
                ToolStatusManager.setToolStatus(appInfo.appId, this.id, ToolStatus.ERROR);
                return;
            }
            HttpPost httpPost = new HttpPost(URL);
            httpPost.setEntity(entity);
            appInfo.log.info(name + " adapter sending app " + appInfo.appId + " to " + URL);

            // Send the app to the tool
            final HttpResponse response = httpclient.execute(httpPost);
            httpPost = null;
            appInfo.log.info(name + " adapter received: " + response.getStatusLine());

            if (protocol == Protocol.SYNCHRONOUS) {
                // We only handle report from Synchronous tools here. Reports for
                // asynchronous tools are handled by the AppVetServlet.
                final HttpEntity responseEntity = response.getEntity();
                final InputStream inputStream = responseEntity.getContent();
                final String reportPath = appInfo.getReportsPath() + "/" + getReportName();
                File fileOut = new File(reportPath);
                FileOutputStream fileOutputStream = new FileOutputStream(fileOut, false);
                int c;
                while ((c = inputStream.read()) != -1) {
                    fileOutputStream.write(c);
                }
                fileOutputStream.flush();
                inputStream.close();
                fileOutputStream.close();
                fileOutputStream = null;
                fileOut = null;

                String toolResult = response.getFirstHeader(appvetResultHeaderName).getValue();
                appInfo.log.debug("Received tool result: " + toolResult + " from " + this.id);

                ToolStatus toolStatus = ToolStatus.getStatus(toolResult);

                if (toolStatus == null) {
                    appInfo.log.error("Error " + "reading report");
                    ToolStatusManager.setToolStatus(appInfo.appId, this.id, ToolStatus.ERROR);
                    return;
                } else {
                    ToolStatusManager.setToolStatus(appInfo.appId, this.id, toolStatus);
                }

            } else if (protocol == Protocol.ASYNCHRONOUS) {

                final String httpResponseVal = response.getStatusLine().toString();
                if ((httpResponseVal.indexOf("HTTP/1.1 202 Accepted") > -1)
                        || (httpResponseVal.indexOf("HTTP/1.1 200 OK") > -1)) {
                    appInfo.log.info("Asynchronous Kryptowire received: " + httpResponseVal);
                } else {
                    appInfo.log.error(
                            "Tool '" + id + "' received: " + httpResponseVal + ". Could not process app. Aborting");
                    ToolStatusManager.setToolStatus(appInfo.appId, this.id, ToolStatus.ERROR);

                }
            }

            entity = null;
            httpclient = null;
            httpParameters = null;
            Date endDate = new Date();
            final long endTime = endDate.getTime();
            endDate = null;
            final long elapsedTime = endTime - startTime;
            appInfo.log.info(name + " elapsed: " + Logger.formatElapsed(elapsedTime));
        } catch (final Exception e) {
            e.printStackTrace();
            appInfo.log.error(e.getMessage());
            ToolStatusManager.setToolStatus(appInfo.appId, this.id, ToolStatus.ERROR);
        }
    }

    public void setApp(AppInfo appInfo) {
        this.appInfo = appInfo;
    }
}