package com.denimgroup.threadfix.service; import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.io.Reader; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; /** * This class is included because it is sometimes useful for these methods to appear * outside of the AbstractChannelImporter. For example, when trying to determine the type * of scanner that produced an uploaded file, the readSAXInput method can be a helpful tool. * * @author mcollins * */ public final class ScanUtils { private static final SanitizedLogger STATIC_LOGGER = new SanitizedLogger(ScanUtils.class); private ScanUtils(){} /** * This method checks through the XML with a blank parser to determine * whether SAX parsing will fail due to an exception. */ public static boolean isBadXml(InputStream inputStream) { try { readSAXInput(new DefaultHandler(), inputStream); } catch (SAXException e) { STATIC_LOGGER.warn("Trying to read XML returned the error " + e.getMessage()); return true; } catch (IOException e) { STATIC_LOGGER.warn("Trying to read XML returned the error " + e.getMessage()); return true; } finally { closeInputStream(inputStream); } return false; } /** * This method with one argument sets up the SAXParser and inputStream correctly * and executes the parsing. With two it adds a completion code and exception handling. * @param handler * @param completionCode */ public static void readSAXInput(DefaultHandler handler, String completionCode, InputStream stream) { try { readSAXInput(handler, stream); } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { if (!e.getMessage().equals(completionCode)) e.printStackTrace(); } finally { closeInputStream(stream); } } public static void closeInputStream(InputStream stream) { if (stream != null) { try { stream.close(); } catch (IOException ex) { STATIC_LOGGER.warn("Closing an input stream failed.", ex); } } } private static void readSAXInput(DefaultHandler handler, InputStream stream) throws SAXException, IOException { XMLReader xmlReader = XMLReaderFactory.createXMLReader(); xmlReader.setContentHandler(handler); xmlReader.setErrorHandler(handler); // Wrapping the inputStream in a BufferedInputStream allows us to mark and reset it BufferedInputStream newStream = new BufferedInputStream(stream); // UTF-8 contains 3 characters at the start of a file, which is a problem. = null; // The SAX parser sees them as characters in the prolog and throws an exception. // This code removes them if they are present. newStream.mark(4); if (newStream.read() == 239) { newStream.read(); newStream.read(); } else newStream.reset(); Reader fileReader = new InputStreamReader(newStream,"UTF-8"); InputSource source = new InputSource(fileReader); source.setEncoding("UTF-8"); xmlReader.parse(source); } public static boolean isZip(String fileName) { RandomAccessFile file = null; try { file = new RandomAccessFile(new File(fileName), "r"); // these are the magic bytes for a zip file return file.readInt() == 0x504B0304; } catch (FileNotFoundException e) { STATIC_LOGGER.warn("The file was not found. Check the usage of this method.", e); } catch (IOException e) { STATIC_LOGGER.warn("IOException. Weird.", e); } finally { if (file != null) { try { file.close(); } catch (IOException e) { STATIC_LOGGER.error("Encountered IOException when attempting to close a file.", e); } } } return false; } }