merge upstream

This commit is contained in:
bjiang
2016-05-03 13:12:05 -04:00
63 changed files with 434 additions and 390 deletions

View File

@@ -24,6 +24,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.owasp.dependencycheck.suppression.SuppressionParseException;
import org.owasp.dependencycheck.suppression.SuppressionParser;
@@ -34,6 +35,7 @@ import org.owasp.dependencycheck.utils.FileUtils;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* Abstract base suppression analyzer that contains methods for parsing the suppression xml file.
@@ -103,6 +105,10 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
try {
rules = parser.parseSuppressionRules(this.getClass().getClassLoader().getResourceAsStream("dependencycheck-base-suppression.xml"));
} catch (SuppressionParseException ex) {
LOGGER.error("Unable to parse the base suppression data file");
LOGGER.debug("Unable to parse the base suppression data file", ex);
} catch (SAXException ex) {
LOGGER.error("Unable to parse the base suppression data file");
LOGGER.debug("Unable to parse the base suppression data file", ex);
}
final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);

View File

@@ -30,6 +30,7 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -220,14 +221,12 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
*/
private String getFileContents(final File actualFile)
throws AnalysisException {
String contents = "";
try {
contents = FileUtils.readFileToString(actualFile).trim();
return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);
}
return contents;
}
/**

View File

@@ -33,6 +33,7 @@ import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
@@ -156,7 +157,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
dependency.setDisplayFileName(String.format("%s%c%s", parentName, File.separatorChar, name));
String contents;
try {
contents = FileUtils.readFileToString(file).trim();
contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);

View File

@@ -28,6 +28,7 @@ import org.owasp.dependencycheck.utils.Settings;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -158,14 +159,12 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
*/
private String getFileContents(final File actualFile)
throws AnalysisException {
String contents;
try {
contents = FileUtils.readFileToString(actualFile).trim();
return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);
}
return contents;
}
@Override

View File

@@ -32,6 +32,7 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -210,7 +211,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
throws AnalysisException {
String contents;
try {
contents = FileUtils.readFileToString(file).trim();
contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);

View File

@@ -20,6 +20,7 @@ package org.owasp.dependencycheck.analyzer;
import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Reference;
@@ -30,10 +31,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.logging.Level;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
/**
* Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party bundle-audit tool.
* Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party
* bundle-audit tool.
*
* @author Dale Visser
*/
@@ -58,6 +63,10 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
public static final String ADVISORY = "Advisory: ";
public static final String CRITICALITY = "Criticality: ";
public CveDB cvedb;
//instance.open();
//Vulnerability result = instance.getVulnerability("CVE-2015-3225");
/**
* @return a filter that accepts files named Gemfile.lock
*/
@@ -83,7 +92,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
final ProcessBuilder builder = new ProcessBuilder(args);
builder.directory(folder);
try {
LOGGER.info("Launching: " + args + " from " + folder);
LOGGER.info("Launching: " + args + " from " + folder);
return builder.start();
} catch (IOException ioe) {
throw new AnalysisException("bundle-audit failure", ioe);
@@ -91,23 +100,34 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
}
/**
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location.
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a
* temporary location.
*
* @throws Exception if anything goes wrong
*/
@Override
public void initializeFileTypeAnalyzer() throws Exception {
// Now, need to see if bundle-audit actually runs from this location.
Process process = null;
try {
process = launchBundleAudit(Settings.getTempDirectory());
}
catch(AnalysisException ae) {
LOGGER.warn("Exception from bundle-audit process: {}. Disabling {}", ae.getCause(), ANALYZER_NAME);
try {
cvedb = new CveDB();
cvedb.open();
} catch (DatabaseException ex) {
LOGGER.warn("Exception opening the database");
LOGGER.debug("error", ex);
setEnabled(false);
throw ex;
}
// Now, need to see if bundle-audit actually runs from this location.
Process process = null;
try {
process = launchBundleAudit(Settings.getTempDirectory());
} catch (AnalysisException ae) {
LOGGER.warn("Exception from bundle-audit process: {}. Disabling {}", ae.getCause(), ANALYZER_NAME);
setEnabled(false);
cvedb.close();
cvedb = null;
throw ae;
}
}
int exitValue = process.waitFor();
if (0 == exitValue) {
LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling {}: {}", ANALYZER_NAME, exitValue);
@@ -135,7 +155,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
}
}
}
if (isEnabled()) {
LOGGER.info(ANALYZER_NAME + " is enabled. It is necessary to manually run \"bundle-audit update\" "
+ "occasionally to keep its database up to date.");
@@ -163,7 +183,8 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
}
/**
* Returns the key used in the properties file to reference the analyzer's enabled property.
* Returns the key used in the properties file to reference the analyzer's
* enabled property.
*
* @return the analyzer's enabled property setting key
*/
@@ -173,8 +194,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
}
/**
* If {@link #analyzeFileType(Dependency, Engine)} is called, then we have successfully initialized, and it will be necessary
* to disable {@link RubyGemspecAnalyzer}.
* If {@link #analyzeFileType(Dependency, Engine)} is called, then we have
* successfully initialized, and it will be necessary to disable
* {@link RubyGemspecAnalyzer}.
*/
private boolean needToDisableGemspecAnalyzer = true;
@@ -205,11 +227,11 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
}
BufferedReader rdr = null;
try {
BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
while(errReader.ready()) {
String error = errReader.readLine();
LOGGER.warn(error);
}
BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
while (errReader.ready()) {
String error = errReader.readLine();
LOGGER.warn(error);
}
rdr = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
processBundlerAuditOutput(dependency, engine, rdr);
} catch (IOException ioe) {
@@ -294,15 +316,23 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
private void addCriticalityToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) {
if (null != vulnerability) {
final String criticality = nextLine.substring(CRITICALITY.length()).trim();
if ("High".equals(criticality)) {
vulnerability.setCvssScore(8.5f);
} else if ("Medium".equals(criticality)) {
vulnerability.setCvssScore(5.5f);
} else if ("Low".equals(criticality)) {
vulnerability.setCvssScore(2.0f);
} else {
vulnerability.setCvssScore(-1.0f);
float score = -1.0f;
Vulnerability v = null;
try {
v = cvedb.getVulnerability(vulnerability.getName());
} catch (DatabaseException ex) {
LOGGER.debug("Unable to look up vulnerability {}", vulnerability.getName());
}
if (v != null) {
score = v.getCvssScore();
} else if ("High".equalsIgnoreCase(criticality)) {
score = 8.5f;
} else if ("Medium".equalsIgnoreCase(criticality)) {
score = 5.5f;
} else if ("Low".equalsIgnoreCase(criticality)) {
score = 2.0f;
}
vulnerability.setCvssScore(score);
}
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
}
@@ -334,7 +364,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
final File gemFile = new File(Settings.getTempDirectory(), gem + "_Gemfile.lock");
gemFile.createNewFile();
final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem);
FileUtils.write(gemFile, displayFileName); // unique contents to avoid dependency bundling
FileUtils.write(gemFile, displayFileName, Charset.defaultCharset()); // unique contents to avoid dependency bundling
final Dependency dependency = new Dependency(gemFile);
dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST);
dependency.setDisplayFileName(displayFileName);

View File

@@ -21,6 +21,7 @@ import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -113,7 +114,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
throws AnalysisException {
String contents;
try {
contents = FileUtils.readFileToString(dependency.getActualFile());
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);

View File

@@ -372,7 +372,7 @@ public class CveDB {
* @return a vulnerability object
* @throws DatabaseException if an exception occurs
*/
private Vulnerability getVulnerability(String cve) throws DatabaseException {
public Vulnerability getVulnerability(String cve) throws DatabaseException {
PreparedStatement psV = null;
PreparedStatement psR = null;
PreparedStatement psS = null;

View File

@@ -25,6 +25,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
import java.util.logging.Level;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
@@ -47,20 +48,24 @@ public class SuppressionParser {
*/
private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionParser.class);
/**
* JAXP Schema Language. Source: http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
* JAXP Schema Language. Source:
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
*/
public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
/**
* W3C XML Schema. Source: http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
* W3C XML Schema. Source:
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
*/
public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
/**
* JAXP Schema Source. Source: http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
* JAXP Schema Source. Source:
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
*/
public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
/**
* Parses the given xml file and returns a list of the suppression rules contained.
* Parses the given xml file and returns a list of the suppression rules
* contained.
*
* @param file an xml file containing suppression rules
* @return a list of suppression rules
@@ -74,6 +79,20 @@ public class SuppressionParser {
} catch (IOException ex) {
LOGGER.debug("", ex);
throw new SuppressionParseException(ex);
} catch (SAXException ex) {
try {
if (fis != null) {
try {
fis.close();
} catch (IOException ex1) {
LOGGER.debug("Unable to close stream", ex1);
}
}
fis = new FileInputStream(file);
} catch (FileNotFoundException ex1) {
throw new SuppressionParseException(ex);
}
return parseOldSuppressionRules(fis);
} finally {
if (fis != null) {
try {
@@ -86,13 +105,62 @@ public class SuppressionParser {
}
/**
* Parses the given xml stream and returns a list of the suppression rules contained.
* Parses the given xml stream and returns a list of the suppression rules
* contained.
*
* @param inputStream an InputStream containing suppression rues
* @return a list of suppression rules
* @throws SuppressionParseException if the xml cannot be parsed
*/
public List<SuppressionRule> parseSuppressionRules(InputStream inputStream) throws SuppressionParseException {
public List<SuppressionRule> parseSuppressionRules(InputStream inputStream) throws SuppressionParseException, SAXException {
try {
final InputStream schemaStream = this.getClass().getClassLoader().getResourceAsStream("schema/dependency-suppression.1.1.xsd");
final SuppressionHandler handler = new SuppressionHandler();
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
final SAXParser saxParser = factory.newSAXParser();
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_LANGUAGE, SuppressionParser.W3C_XML_SCHEMA);
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_SOURCE, new InputSource(schemaStream));
final XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setErrorHandler(new SuppressionErrorHandler());
xmlReader.setContentHandler(handler);
final Reader reader = new InputStreamReader(inputStream, "UTF-8");
final InputSource in = new InputSource(reader);
//in.setEncoding("UTF-8");
xmlReader.parse(in);
return handler.getSuppressionRules();
} catch (ParserConfigurationException ex) {
LOGGER.debug("", ex);
throw new SuppressionParseException(ex);
} catch (SAXException ex) {
if (ex.getMessage().contains("Cannot find the declaration of element 'suppressions'.")) {
throw ex;
} else {
LOGGER.debug("", ex);
throw new SuppressionParseException(ex);
}
} catch (FileNotFoundException ex) {
LOGGER.debug("", ex);
throw new SuppressionParseException(ex);
} catch (IOException ex) {
LOGGER.debug("", ex);
throw new SuppressionParseException(ex);
}
}
/**
* Parses the given xml stream and returns a list of the suppression rules
* contained.
*
* @param inputStream an InputStream containing suppression rues
* @return a list of suppression rules
* @throws SuppressionParseException if the xml cannot be parsed
*/
private List<SuppressionRule> parseOldSuppressionRules(InputStream inputStream) throws SuppressionParseException {
try {
final InputStream schemaStream = this.getClass().getClassLoader().getResourceAsStream("schema/suppression.xsd");
final SuppressionHandler handler = new SuppressionHandler();

View File

@@ -0,0 +1,36 @@
if exists (SELECT 1 FROM sysobjects WHERE name='software' AND xtype='U')
drop table software
if exists (SELECT 1 FROM sysobjects WHERE name='cpeEntry' AND xtype='U')
drop table cpeEntry
if exists (SELECT 1 FROM sysobjects WHERE name='reference' AND xtype='U')
drop table reference
if exists (SELECT 1 FROM sysobjects WHERE name='vulnerability' AND xtype='U')
drop table vulnerability
if exists (SELECT 1 FROM sysobjects WHERE name='properties' AND xtype='U')
drop table properties
CREATE TABLE properties (id varchar(50) PRIMARY KEY, value varchar(500));
CREATE TABLE vulnerability (id int identity(1,1) PRIMARY KEY, cve VARCHAR(20) UNIQUE,
description VARCHAR(8000), cwe VARCHAR(10), cvssScore DECIMAL(3,1), cvssAccessVector VARCHAR(20),
cvssAccessComplexity VARCHAR(20), cvssAuthentication VARCHAR(20), cvssConfidentialityImpact VARCHAR(20),
cvssIntegrityImpact VARCHAR(20), cvssAvailabilityImpact VARCHAR(20));
CREATE TABLE reference (cveid INT, name VARCHAR(1000), url VARCHAR(1000), source VARCHAR(255),
CONSTRAINT FK_Reference FOREIGN KEY (cveid) REFERENCES vulnerability(id) ON DELETE CASCADE);
CREATE TABLE cpeEntry (id INT identity(1,1) PRIMARY KEY, cpe VARCHAR(250), vendor VARCHAR(255), product VARCHAR(255));
CREATE TABLE software (cveid INT, cpeEntryId INT, previousVersion VARCHAR(50)
, CONSTRAINT FK_SoftwareCve FOREIGN KEY (cveid) REFERENCES vulnerability(id) ON DELETE CASCADE
, CONSTRAINT FK_SoftwareCpeProduct FOREIGN KEY (cpeEntryId) REFERENCES cpeEntry(id)
, PRIMARY KEY (cveid, cpeEntryId));
CREATE INDEX idxVulnerability ON vulnerability(cve);
CREATE INDEX idxReference ON reference(cveid);
CREATE INDEX idxCpe ON cpeEntry(cpe);
CREATE INDEX idxCpeEntry ON cpeEntry(vendor, product);
CREATE INDEX idxSoftwareCve ON software(cveid);
CREATE INDEX idxSoftwareCpe ON software(cpeEntryId);
INSERT INTO properties(id,value) VALUES ('version','3.0');

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression">
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress base="true">
<notes><![CDATA[
This suppresses false positives identified on spring security.
@@ -298,6 +298,13 @@
<gav regex="true">io\.dropwizard\.metrics:metrics-httpclient:.*</gav>
<cpe>cpe:/a:apache:httpclient</cpe>
</suppress>
<suppress base="true">
<notes><![CDATA[
javax.transaction false positives
]]></notes>
<gav regex="true">javax\.transaction:javax\.transaction-api:.*</gav>
<cpe>cpe:/a:oracle:glassfish</cpe>
</suppress>
<suppress base="true">
<notes><![CDATA[
false positive in drop wizard

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema id="suppressions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd"
xmlns:dc="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<xs:complexType name="regexStringType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="regex" use="optional" type="xs:boolean" default="false"/>
<xs:attribute name="caseSensitive" use="optional" type="xs:boolean" default="false"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="cvssScoreType">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="cveType">
<xs:restriction base="xs:string">
<xs:pattern value="((\w+\-)?CVE\-\d\d\d\d\-\d+|\d+)"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="sha1Type">
<xs:restriction base="xs:string">
<xs:pattern value="[a-fA-F0-9]{40}"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="suppressions">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="suppress">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="1">
<xs:sequence minOccurs="0" maxOccurs="1">
<xs:element name="notes" type="xs:string"/>
</xs:sequence>
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="filePath" type="dc:regexStringType"/>
<xs:element name="sha1" type="dc:sha1Type"/>
<xs:element name="gav" type="dc:regexStringType"/>
</xs:choice>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="cpe" type="dc:regexStringType"/>
<xs:element name="cve" type="dc:cveType"/>
<xs:element name="cwe" type="xs:positiveInteger"/>
<xs:element name="cvssBelow" type="dc:cvssScoreType"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="base" use="optional" type="xs:boolean" default="false"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -56,4 +56,4 @@
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</xs:schema>

View File

@@ -79,7 +79,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
setTimeout('$("#modal-content,#modal-background").toggleClass("active");',100);
});
$('#modal-add-header').click(function () {
xml = '<?xml version="1.0" encoding="UTF-8"?>\n<suppressions xmlns="https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression">\n ';
xml = '<?xml version="1.0" encoding="UTF-8"?>\n<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">\n ';
xml += $("#modal-text").text().replace(/\n/g,'\n ');
xml += '\n</suppressions>';
$('#modal-text').text(xml).focus().select();