gov.nist.appvet.tool.SynchronousService.java Source code

Java tutorial

Introduction

Here is the source code for gov.nist.appvet.tool.SynchronousService.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.tool;

import gov.nist.appvet.tool.util.Logger;
import gov.nist.appvet.tool.util.Native;
import gov.nist.appvet.tool.util.TimeoutException;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * This class implements a synchronous tool service.
 */
public class SynchronousService extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final Logger log = Properties.log;

    public SynchronousService() {
        super();
        log.info("Android Signature Verifier service ready");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        FileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        List items = null;
        FileItem fileItem = null;

        try {
            items = upload.parseRequest(request);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }

        // Get form fields
        Iterator iter = items.iterator();
        FileItem item = null;
        while (iter.hasNext()) {
            item = (FileItem) iter.next();
            if (item.isFormField()) {
                //String incomingParameter = item.getFieldName();
                //String incomingValue = item.getString();
                // For this service, we do not expect any input parameters 
                // except for the app file.
                //log.debug("Ignoring " + incomingParameter + " = " + incomingValue);
            } else {
                // item should now hold the received file
                if (item != null) {
                    fileItem = item;
                    log.debug("Received file: " + fileItem.getName());
                }
            }
        }

        String appFilePath = null;
        String fileName = null;
        Random rand = new Random(new Date().getTime());
        final int randInt = rand.nextInt(9999999);
        String id = new Integer(randInt).toString();

        if (fileItem != null) {
            fileName = getFileName(fileItem.getName());
            if (!fileName.endsWith(".apk")) {
                sendHttp400(response, "Invalid app file: " + fileItem.getName());
                return;
            }

            appFilePath = Properties.TEMP_DIR + "/" + id + fileName;
            log.debug("App file path: " + appFilePath);

            if (!saveFileUpload(fileItem, appFilePath)) {
                sendHttp500(response, "Could not save uploaded file");
                return;
            }
        } else {
            sendHttp400(response, "No app was received.");
            return;
        }

        String command = Properties.command + " " + appFilePath;

        StringBuffer reportBuffer = new StringBuffer();
        boolean succeeded = runTool(command, reportBuffer);
        if (!succeeded) {
            if (reportBuffer.toString().contains("java.lang.SecurityException")) {
                returnReport(response, fileName, ToolStatus.FAIL, reportBuffer.toString());
            } else {
                log.error("Error detected: " + reportBuffer.toString());
                returnReport(response, fileName, ToolStatus.ERROR, reportBuffer.toString());
            }
        } else {
            log.debug("Analyzing report for " + appFilePath);
            ToolStatus reportStatus = analyzeReport(reportBuffer.toString());
            log.debug("Result: " + reportStatus.name());
            returnReport(response, fileName, reportStatus, reportBuffer.toString());
        }

        boolean deleted = deleteFile(appFilePath);
        if (deleted) {
            log.debug("Deleted " + appFilePath);
        } else {
            log.error("Could not delete file " + appFilePath);
        }

        // Clean up
        reportBuffer = null;
        System.gc();
    }

    public synchronized boolean runTool(String newCommand, StringBuffer output) {
        return execute(newCommand, output);
    }

    public static String getFileName(String filePath) {
        String fileName = null;
        int lastBackSlash = filePath.lastIndexOf("\\");
        int lastForwardSlash = filePath.lastIndexOf("/");

        if (lastBackSlash == -1 && lastForwardSlash == -1)
            // No slashes found in file path
            fileName = filePath;
        else if (lastBackSlash >= 0)
            // Back slash detected
            fileName = filePath.substring(lastBackSlash + 1, filePath.length());
        else
            // Forward slash detected
            fileName = filePath.substring(lastForwardSlash + 1, filePath.length());
        return fileName;
    }

    private void sendHttp400(HttpServletResponse response, String message) {
        try {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println(message);
            out.flush();
            out.close();
            log.debug("Returned HTTP 400: " + message);
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private void sendHttp500(HttpServletResponse response, String message) {
        try {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println(message);
            out.flush();
            out.close();
            log.debug("Returned HTTP 500: " + message);
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    public boolean saveFileUpload(FileItem fileItem, String outputFilePath) {
        try {
            if (fileItem == null) {
                log.error("File item is NULL");
                return false;
            }
            File file = new File(outputFilePath);
            fileItem.write(file);
            log.debug("Saved file " + outputFilePath);
            return true;
        } catch (IOException e) {
            log.error(e.getMessage());
            return false;
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }
    }

    private synchronized boolean execute(String cmd, StringBuffer output) {
        long timeout = 60000; // 10 mins
        try {
            return Native.exec(cmd, timeout, true, output);
        } catch (TimeoutException e) {
            log.error(e.getMessage());
            output.append(e.getMessage());
            return false;
        } catch (IOException e) {
            log.error(e.getMessage());
            output.append(e.getMessage());
            return false;
        }
    }

    public ToolStatus analyzeReport(String report) {
        if (report == null || report.isEmpty()) {
            log.error("Report is null or empty.");
            return ToolStatus.ERROR;
        }

        // Scan file for result strings defined in configuration file. Here,
        // we always scan in this order: ERRORs, FAILs, WARNINGs, and PASSs.
        if (Properties.errorResults != null && !Properties.errorResults.isEmpty()) {
            for (String s : Properties.errorResults) {
                if (report.indexOf(s) > -1) {
                    return ToolStatus.ERROR;
                }
            }
        }

        if (Properties.failResults != null && !Properties.failResults.isEmpty()) {
            for (String s : Properties.failResults) {
                if (report.indexOf(s) > -1) {
                    return ToolStatus.FAIL;
                }
            }
        }

        if (Properties.warningResults != null && !Properties.warningResults.isEmpty()) {
            for (String s : Properties.warningResults) {
                if (report.indexOf(s) > -1) {
                    return ToolStatus.WARNING;
                }
            }
        }

        if (Properties.passResults != null && !Properties.passResults.isEmpty()) {
            for (String s : Properties.passResults) {
                if (report.indexOf(s) > -1) {
                    return ToolStatus.PASS;
                }
            }
        }

        return Properties.defaultStatus;
    }

    // TODO Return Common Vulnerability Scoring System (CVSS) here in lieu of
    // statuses.
    public void returnReport(HttpServletResponse response, String fileName, ToolStatus reportStatus,
            String report) {

        StringBuffer htmlBuffer = new StringBuffer();
        htmlBuffer.append("<HTML>\n");
        htmlBuffer.append("<head>\n");
        htmlBuffer.append("<style type=\"text/css\">\n");
        htmlBuffer.append("h3 {font-family:arial;}\n");
        htmlBuffer.append("h4 {font-family:arial;}\n");
        htmlBuffer.append("p {font-family:arial;}\n");
        htmlBuffer.append("</style>\n");
        htmlBuffer.append("<title>Signature Verifier Report</title>\n");
        htmlBuffer.append("</head>\n");
        htmlBuffer.append("<body>\n");
        String appVetImagesUrl = Properties.mainURL + "/images/nist-gray.png";

        htmlBuffer.append("<img border=\"0\" width=\"100px\" src=\"" + appVetImagesUrl + "\" alt=\"appvet\" />");
        htmlBuffer.append("<HR>\n");
        htmlBuffer.append("<h3>Signature Verifier Report</h3>\n");
        htmlBuffer.append("<pre>\n");
        htmlBuffer.append("File: \t\t" + fileName + "\n");
        final Date date = new Date();
        final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss.SSSZ");
        final String currentDate = format.format(date);
        htmlBuffer.append("Date: \t\t" + currentDate + "\n\n");
        if (reportStatus == ToolStatus.PASS) {
            htmlBuffer.append("Status: \t<font color=\"green\">" + reportStatus.name() + "</font>\n");
            htmlBuffer.append(
                    "Description: \tApp is signed (Note: some warnings may exist. See below for details).\n\n");
        } else {
            htmlBuffer.append("Status: \t<font color=\"red\">" + reportStatus.name() + "</font>\n");
            htmlBuffer.append("Description: \tApp is unsigned or incorrectly signed.\n\n");
        }
        htmlBuffer.append("<hr>");
        htmlBuffer.append("<h4>Details</h4>");
        htmlBuffer.append(report);
        htmlBuffer.append("</body>\n");
        htmlBuffer.append("</HTML>\n");

        try {
            response.setStatus(HttpServletResponse.SC_OK); // HTTP 200
            response.setContentType("text/html");
            response.setHeader("toolrisk", reportStatus.name());
            PrintWriter out = response.getWriter();
            out.println(htmlBuffer.toString());
            out.flush();
            out.close();
            log.debug("Returned report");
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    public boolean deleteFile(String sourceFilePath) {
        File file = new File(sourceFilePath);
        if (file.exists()) {
            return file.delete();
        } else {
            log.warn("File " + sourceFilePath + " does not exist");
            return false;
        }
    }
}