//////////////////////////////////////////////////////////////////////// // // 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.channel; import java.util.AbstractMap.SimpleEntry; import java.util.HashSet; import java.util.Map.Entry; import java.util.Set; 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.ChannelVulnerability; import com.denimgroup.threadfix.data.entities.Finding; import com.denimgroup.threadfix.data.entities.Scan; import com.denimgroup.threadfix.webapp.controller.ScanCheckResultBean; /** * * @author mcollins */ public class ZaproxyChannelImporter extends AbstractChannelImporter { private static final String SQL_INJECTION = "SQL Injection", XSS = "Cross Site Scripting"; private static final Set<Entry<String[], String>> alternativesMap = new HashSet<>(); private static void addToSet(String[] array, String key) { alternativesMap.add(new SimpleEntry<String[], String>(array, key)); } static { addToSet(new String[] {"sql", "injection"}, SQL_INJECTION); addToSet(new String[] {"sqli"}, SQL_INJECTION); addToSet(new String[] {"cross", "site", "scripting"}, XSS); addToSet(new String[] {"xss"}, XSS); } @Autowired public ZaproxyChannelImporter(ChannelTypeDao channelTypeDao, ChannelVulnerabilityDao channelVulnerabilityDao, ChannelSeverityDao channelSeverityDao) { this.channelTypeDao = channelTypeDao; this.channelVulnerabilityDao = channelVulnerabilityDao; this.channelSeverityDao = channelSeverityDao; this.channelType = channelTypeDao.retrieveByName(ChannelType.ZAPROXY); } @Override public Scan parseInput() { return parseSAXInput(new ZaproxySAXParser()); } private String getAlternative(String possibility) { String lower = possibility.toLowerCase(); MAP: for (Entry<String[], String> entry : alternativesMap) { for (String key : entry.getKey()) { if (!lower.contains(key)) { continue MAP; } } // if we get here then the string contains all the keys return entry.getValue(); } return null; } public class ZaproxySAXParser extends HandlerWithBuilder { private Boolean getDate = false; private Boolean getUri = false; private Boolean getParameter = false; private Boolean getChannelVulnName = false; private Boolean getSeverityName = 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 ("report".equals(qName)) { getDate = true; } else if ("OWASPZAPReport".equals(qName)) { date = getCalendarFromString("EEE, dd MMM yyyy kk:mm:ss", atts.getValue("generated")); } else if ("uri".equals(qName)) { getUri = true; } else if ("alert".equals(qName)) { getChannelVulnName = true; } else if ("param".equals(qName)) { getUri = false; getParameter = true; } else if ("riskcode".equals(qName)) { getSeverityName = true; } else if ("otherinfo".equals(qName)) { Finding finding = constructFinding(currentPath, currentParameter, currentChannelVulnCode, currentSeverityCode); if (finding != null && finding.getChannelVulnerability() == null) { String channelVulnerabilityCode = getAlternative(currentChannelVulnCode); if (channelVulnerabilityCode != null) { ChannelVulnerability channelVulnerability = getChannelVulnerability(channelVulnerabilityCode); finding.setChannelVulnerability(channelVulnerability); } } add(finding); currentParameter = null; currentPath = null; getParameter = false; } } public void endElement (String uri, String name, String qName) { if ("report".equals(qName)) { getDate = false; } else if ("alertitem".equals(qName)) { currentChannelVulnCode = null; currentSeverityCode = null; } else if (getUri) { currentPath = getBuilderText(); getUri = false; } else if (getChannelVulnName) { currentChannelVulnCode = getBuilderText(); getChannelVulnName = false; } else if (getParameter) { currentParameter = getBuilderText(); if (currentParameter != null && currentParameter.contains("=")) currentParameter = currentParameter.substring(0,currentParameter.indexOf("=")); getParameter = false; } else if ("riskcode".equals(qName)) { currentSeverityCode = getBuilderText(); getSeverityName = false; } else if (getDate) { String tempDateString = getBuilderText(); String anchorString = "Report generated at "; if (tempDateString != null && !tempDateString.trim().isEmpty() && tempDateString.contains(anchorString)) { tempDateString = tempDateString.substring(tempDateString.indexOf(anchorString) + anchorString.length(),tempDateString.length()-2); date = getCalendarFromString("EEE, dd MMM yyyy kk:mm:ss", tempDateString); } getDate = false; } } public void characters (char ch[], int start, int length) { if (getDate || getParameter || getUri || getChannelVulnName || getSeverityName) { addTextToBuilder(ch,start,length); } } } @Override public ScanCheckResultBean checkFile() { return testSAXInput(new ZaproxySAXValidator()); } public class ZaproxySAXValidator extends HandlerWithBuilder { private boolean hasFindings = false; private boolean hasDate = false; private boolean correctFormat = false; private boolean getDate = false; private void setTestStatus() { if (!correctFormat) testStatus = ScanImportStatus.WRONG_FORMAT_ERROR; else if (hasDate) testStatus = checkTestDate(); if ((testStatus == null || ScanImportStatus.SUCCESSFUL_SCAN == testStatus) && !hasFindings) testStatus = ScanImportStatus.EMPTY_SCAN_ERROR; else if (testStatus == null) testStatus = ScanImportStatus.SUCCESSFUL_SCAN; } //////////////////////////////////////////////////////////////////// // Event handlers. //////////////////////////////////////////////////////////////////// public void endDocument() { setTestStatus(); } public void startElement (String uri, String name, String qName, Attributes atts) throws SAXException { if (getDate) { String tempDateString = getBuilderText(); String anchorString = "Report generated at "; if (tempDateString != null && !tempDateString.trim().isEmpty() && tempDateString.contains(anchorString)) { tempDateString = tempDateString.substring(tempDateString.indexOf(anchorString) + anchorString.length(),tempDateString.length()-2); testDate = getCalendarFromString("EEE, dd MMM yyyy kk:mm:ss", tempDateString); if (testDate != null) { hasDate = true; } } getDate = false; } if ("report".equals(qName)) { getDate = true; correctFormat = true; } if ("OWASPZAPReport".equals(qName)) { String tempDateString = atts.getValue("generated"); String anchorString = "Report generated at "; if (tempDateString != null && !tempDateString.trim().isEmpty() && tempDateString.contains(anchorString)) { tempDateString = tempDateString.substring(tempDateString.indexOf(anchorString) + anchorString.length(),tempDateString.length()-2); testDate = getCalendarFromString("EEE, dd MMM yyyy kk:mm:ss", tempDateString); if (testDate != null) { hasDate = true; } } correctFormat = true; } if ("alertitem".equals(qName)) { hasFindings = true; setTestStatus(); throw new SAXException(FILE_CHECK_COMPLETED); } } public void characters (char ch[], int start, int length) { if (getDate) { addTextToBuilder(ch,start,length); } } } }