/* * * Paros and its related class files. * * Paros is an HTTP/HTTPS proxy for assessing web application security. * Copyright (C) 2003-2004 Chinotec Technologies Company * * This program is free software; you can redistribute it and/or * modify it under the terms of the Clarified Artistic License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Clarified Artistic License for more details. * * You should have received a copy of the Clarified Artistic License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // ZAP: 2011/10/01 Fixed filename problem (issue 161) // ZAP: 2012/01/24 Changed outer XML (issue 268) c/o Alla // ZAP: 2012/03/15 Changed the methods getAlertXML and generate to use the class // StringBuilder instead of StringBuffer. // ZAP: 2012/04/25 Added @Override annotation to all appropriate methods. // ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments // ZAP: 2013/07/12 Issue 713: Add CWE and WASC numbers to issues // ZAP: 2013/12/03 Issue 933: Automatically determine install dir // ZAP: 2014/07/15 Issue 1263: Generate Report Clobbers Existing Files Without Prompting // ZAP: 2015/11/18 Issue 1555: Rework inclusion of HTML tags in reports // ZAP: 2016/09/22 Issue 2886: Support Markdown format package org.parosproxy.paros.extension.report; import java.io.File; import java.text.MessageFormat; import java.util.Locale; import javax.swing.JFileChooser; import javax.swing.filechooser.FileFilter; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.extension.Extension; import org.parosproxy.paros.extension.ExtensionLoader; import org.parosproxy.paros.extension.ViewDelegate; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.model.SiteMap; import org.parosproxy.paros.model.SiteNode; import org.parosproxy.paros.view.View; import org.zaproxy.zap.extension.XmlReporterExtension; import org.zaproxy.zap.utils.DesktopUtils; import org.zaproxy.zap.utils.XMLStringUtil; import org.zaproxy.zap.view.ScanPanel; import org.zaproxy.zap.view.widgets.WritableFileChooser; public class ReportLastScan { private Logger logger = Logger.getLogger(ReportLastScan.class); private static final String HTM_FILE_EXTENSION=".htm"; private static final String HTML_FILE_EXTENSION=".html"; private static final String XML_FILE_EXTENSION=".xml"; private static final String MD_FILE_EXTENSION=".md"; public enum ReportType {HTML, XML, MD} public ReportLastScan() { } public File generate(String fileName, Model model, String xslFile) throws Exception { StringBuilder sb = new StringBuilder(500); this.generate(sb, model); return ReportGenerator.stringToHtml(sb.toString(), xslFile, fileName); } public void generate(StringBuilder report, Model model) throws Exception { report.append("<?xml version=\"1.0\"?>"); report.append("<OWASPZAPReport version=\"").append(Constant.PROGRAM_VERSION).append("\" generated=\"").append(ReportGenerator.getCurrentDateTimeString()).append("\">\r\n"); siteXML(report); report.append("</OWASPZAPReport>"); } private void siteXML(StringBuilder report) { SiteMap siteMap = Model.getSingleton().getSession().getSiteTree(); SiteNode root = (SiteNode) siteMap.getRoot(); int siteNumber = root.getChildCount(); for (int i = 0; i < siteNumber; i++) { SiteNode site = (SiteNode) root.getChildAt(i); String siteName = ScanPanel.cleanSiteName(site, true); String[] hostAndPort = siteName.split(":"); boolean isSSL = (site.getNodeName().startsWith("https")); String siteStart = "<site name=\"" + XMLStringUtil.escapeControlChrs(site.getNodeName()) + "\"" + " host=\"" + XMLStringUtil.escapeControlChrs(hostAndPort[0])+ "\""+ " port=\"" + XMLStringUtil.escapeControlChrs(hostAndPort[1])+ "\""+ " ssl=\"" + String.valueOf(isSSL) + "\"" + ">"; StringBuilder extensionsXML = getExtensionsXML(site); String siteEnd = "</site>"; report.append(siteStart); report.append(extensionsXML); report.append(siteEnd); } } public StringBuilder getExtensionsXML(SiteNode site) { StringBuilder extensionXml = new StringBuilder(); ExtensionLoader loader = Control.getSingleton().getExtensionLoader(); int extensionCount = loader.getExtensionCount(); for(int i=0; i<extensionCount; i++) { Extension extension = loader.getExtension(i); if(extension instanceof XmlReporterExtension) { extensionXml.append(((XmlReporterExtension)extension).getXml(site)); } } return extensionXml; } /** * Generates a report. Defaults to HTML report if reportType is null. * @param view * @param model * @param reportType */ public void generateReport(ViewDelegate view, Model model, ReportType reportType){ // ZAP: Allow scan report file name to be specified final ReportType localReportType; if(reportType == null) { localReportType=ReportType.HTML; } else { localReportType=reportType; } try { JFileChooser chooser = new WritableFileChooser(Model.getSingleton().getOptionsParam().getUserDirectory()); chooser.setFileFilter(new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } else if (file.isFile()) { String lcFileName=file.getName().toLowerCase(Locale.ROOT); switch (localReportType) { case XML: return lcFileName.endsWith(XML_FILE_EXTENSION); case MD: return lcFileName.endsWith(MD_FILE_EXTENSION); case HTML: default: return (lcFileName.endsWith(HTM_FILE_EXTENSION) || lcFileName.endsWith(HTML_FILE_EXTENSION)); } } return false; } @Override public String getDescription() { switch(localReportType) { case XML: return Constant.messages.getString("file.format.xml"); case MD: return Constant.messages.getString("file.format.md"); case HTML: default: return Constant.messages.getString("file.format.html"); } } }); String reportXSL=""; String fileExtension=""; switch(localReportType) { case XML: fileExtension=XML_FILE_EXTENSION; reportXSL = null; // Dont use XSLT break; case MD: fileExtension=MD_FILE_EXTENSION; reportXSL = (Constant.getZapInstall() + "/xml/report.md.xsl"); break; case HTML: default: fileExtension=HTML_FILE_EXTENSION; reportXSL = (Constant.getZapInstall() + "/xml/report.html.xsl"); break; } chooser.setSelectedFile(new File(fileExtension)); //Default the filename to a reasonable extension; int rc = chooser.showSaveDialog(View.getSingleton().getMainFrame()); File file = null; if (rc == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); File report = generate(file.getAbsolutePath(), model, reportXSL); if (report == null) { view.showMessageDialog( MessageFormat.format(Constant.messages.getString("report.unknown.error"), new Object[]{file.getAbsolutePath()})); return; } try { DesktopUtils.openUrlInBrowser(report.toURI()); } catch (Exception e) { logger.error(e.getMessage(), e); view.showMessageDialog( MessageFormat.format(Constant.messages.getString("report.complete.warning"), new Object[]{report.getAbsolutePath()})); } } } catch (Exception e) { logger.error(e.getMessage(), e); view.showWarningDialog(Constant.messages.getString("report.unexpected.error")); } } /** * @deprecated * generateHtml has been deprecated in favor of using {@link #generateReport(ViewDelegate, Model, ReportType)} */ @Deprecated public void generateHtml(ViewDelegate view, Model model) { generateReport(view, model, ReportType.HTML); } /** * @deprecated * generateXml has been deprecated in favor of using {@link #generateReport(ViewDelegate, Model, ReportType)} */ @Deprecated public void generateXml(ViewDelegate view, Model model) { generateReport(view, model, ReportType.XML); } }