/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.vulnscand;
import org.apache.regexp.RE;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.model.OnmsSeverity;
/**
* <p>NessusParser class.</p>
*
* @author ranger
* @version $Id: $
*/
public class NessusParser {
private static NessusParser instance;
// Lots o' regular expressions
private static RE cveId = null;
private static RE cveString = null;
private static RE riskFactor = null;
private static RE semicolonSingle = null;
private static RE semicolonMulti = null;
private static RE multipleAsterisks = null;
private static RE nessusLine = null;
private static RE nessusSentence = null;
private static RE nessusTag = null;
private static RE nessusInside = null;
// private static RE noPeriod = null;
private static RE tooManyBreaks = null;
private static RE leadingOrTrailingBreaks = null;
private static RE protocolWithPort = null;
private static RE protocolWithoutPort = null;
private static RE indeterminate = null;
// private static RE cleared = null; // Not applicable
private static RE informational = null;
private static RE normal = null;
private static RE normal2 = null;
private static RE warning = null;
private static RE minor = null;
private static RE critical = null;
private static RE major = null;
private static RE greaterThan = null;
private static RE lessThan = null;
private static RE greaterThanToken = null;
private static RE lessThanToken = null;
private static final String GREATER_THAN_TOKEN = "GREATERTHANSIGN";
private static final String GREATER_THAN_ENTITY = ">";
private static final String LESS_THAN_TOKEN = "LESSTHANSIGN";
private static final String LESS_THAN_ENTITY = "<";
private NessusParser() {
initREs();
}
private void initREs() {
// The object takes the performance hit of compiling all of these
// expressions at once, but that should be the only time they are
// compiled
try {
// Wipe any asterisks following a carriage return also
semicolonSingle = new RE("[:space:]*;([:space:]|\\*)*");
semicolonMulti = new RE("([:space:]*;){2,}([:space:]|\\*)*");
// There are lots of multiple asterisks in the messages for emphasis
multipleAsterisks = new RE("([:space:]*\\*){2,}[:space:]*");
// Replace all Nessus product names for cleaner integration
nessusLine = new RE("^[Nn]essus[Dd]{0,1}");
nessusSentence = new RE("\\.([:space:]|;)*[Nn]essus[Dd]{0,1}");
nessusTag = new RE(">([:space:]|;)*[Nn]essus[Dd]{0,1}");
nessusInside = new RE("[Nn]essus[Dd]{0,1}");
// No period preceeding a <br /> tag
// noPeriod = new RE("([A-z0-9]{1})[:space:]*<br />");
// CVE ID numbers
// Can begin with "CVE" or "CAN"
cveId = new RE("[Cc](([Vv][Ee])|([Aa][Nn]))-[0-9]{4}-[0-9]{4}");
// CVE ID number string
cveString = new RE("[:space:]*[Cc][Vv][Ee][:space:]*:[:space:]*([^;]*)");
// Risk factor string
riskFactor = new RE("[:space:]*[Rr]isk[:space:]*[Ff]actor[:space:]*:([:space:]|;)*([^;{1,2}]*)");
//Risk factor :;;None
informational = new RE("([Ii]nfo)");
normal = new RE("([Nn]one)");
normal2 = new RE("(;;[Nn]one)");
warning = new RE("([Ll]ow)");
minor = new RE("([Mm]edium)");
major = new RE("([Hh]igh)");
critical = new RE("([Cc]ritical)|([Ss]erious)");
// Duplicate, trailing <br /> tags
tooManyBreaks = new RE("(<br />){3,}");
leadingOrTrailingBreaks = new RE("((<br />)+$)|(^(<br />)+)");
// Protocol strings, with and without port number
protocolWithPort = new RE("[:space:]*[:alnum:]+[:space:]\\(([:alnum:]+)/([:alnum:]+)\\)[:space:]*");
protocolWithoutPort = new RE("[:space:]*[:alnum:]+/([:alnum:]+)[:space:]*");
greaterThan = new RE(">");
lessThan = new RE("<");
greaterThanToken = new RE(GREATER_THAN_TOKEN);
lessThanToken = new RE(LESS_THAN_TOKEN);
} catch (org.apache.regexp.RESyntaxException ex) {
log().error("Regex syntax error in NessusScan.java. Correct this error and rebuild: " + ex, ex);
}
}
/**
* <p>Getter for the field <code>instance</code>.</p>
*
* @return a {@link org.opennms.netmgt.vulnscand.NessusParser} object.
*/
public static NessusParser getInstance() {
if (instance == null) {
instance = new NessusParser();
}
return instance;
}
/**
* <p>main</p>
*
* @param argv an array of {@link java.lang.String} objects.
*/
public static void main(String[] argv) {
// Little tester function used to parse events
NessusParser myParser = NessusParser.getInstance();
System.out.println(myParser.parseDescr(argv[0]));
}
/**
* <p>parsePort</p>
*
* @param portString a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.vulnscand.PortValues} object.
* @throws java.lang.IllegalArgumentException if any.
*/
public PortValues parsePort(String portString) throws IllegalArgumentException {
PortValues retval = new PortValues();
// If the portString has port and protocol info...
if (protocolWithPort.match(portString)) {
try {
retval.port = Integer.parseInt(protocolWithPort.getParen(1).trim());
} catch (NumberFormatException ex) {
log().error("Cannot parse port into an integer: " + portString, ex);
}
retval.protocol = protocolWithPort.getParen(2).trim();
}
// Otherwise, if it is just a general info...
else if (protocolWithoutPort.match(portString)) {
retval.protocol = protocolWithoutPort.getParen(1).trim();
} else {
// Don't know what this is...
log().error("Invalid service/port/protocol marker in the message: " + portString);
}
if (retval.isValid())
return retval;
else
throw new IllegalArgumentException("String could not be parsed into a PortValues object");
}
/**
* <p>parseDescr</p>
*
* @param descr a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.vulnscand.DescrValues} object.
* @throws java.lang.IllegalArgumentException if any.
*/
public DescrValues parseDescr(String descr) throws IllegalArgumentException {
// Specific fields to be parsed out of the descr
// Descr construction helpers
String risk;
DescrValues retval = new DescrValues();
// Remove stray carriage returns
descr = descr.replace('\n', ' ');
// Locate useful information within the descr string
// Get the risk factor
if (riskFactor.match(descr)) {
risk = riskFactor.getParen(2).trim();
String risk2 = descr;
if (log().isDebugEnabled()) {
log().debug("Descr Parsed: " + risk);
log().debug("Descr Parsed: " + risk2);
}
if (informational.match(risk)) {
retval.severity = OnmsSeverity.NORMAL.getId();
}
if (normal.match(risk)) {
retval.severity = OnmsSeverity.NORMAL.getId();
}
if (normal2.match(risk)) {
retval.severity = OnmsSeverity.NORMAL.getId();
}
if (warning.match(risk)) {
retval.severity = OnmsSeverity.WARNING.getId();
}
if (minor.match(risk)) {
retval.severity = OnmsSeverity.MINOR.getId();
}
if (major.match(risk)) {
retval.severity = OnmsSeverity.MAJOR.getId();
}
if (critical.match(risk)) {
retval.severity = OnmsSeverity.CRITICAL.getId();
}
// If we could not locate a severity in the string...
if (retval.severity == 0) {
// Set it to be indeterminate
retval.severity = OnmsSeverity.INDETERMINATE.getId();
}
// Clear the severity line so that
// it can be added back later with some nicer
// formatting
descr = riskFactor.subst(descr, "");
} else {
risk = null;
retval.severity = OnmsSeverity.INDETERMINATE.getId();
}
// Get the CVE string
if (cveString.match(descr)) {
retval.cveEntry = cveString.getParen(1).trim();
// Validate the CVE ID number
if (!cveId.match(retval.cveEntry)) {
retval.cveEntry = null;
}
// Clear the CVE line so that it can be
// added back later with some nicer
// formatting
else {
descr = cveString.subst(descr, "");
}
} else {
retval.cveEntry = null;
}
// Formatting/beautification changes
// Remove multiple asterisks
descr = multipleAsterisks.subst(descr, " ");
// Change any markup in the text to string tokens
// that will later be changed to HTML entities
// (we can't change them to entities directly
// because ';' is a reserved character in Nessus
// messages).
descr = greaterThan.subst(descr, GREATER_THAN_TOKEN);
descr = lessThan.subst(descr, LESS_THAN_TOKEN);
// Remove Nessus branding from messages
descr = nessusLine.subst(descr, "The scanner");
descr = nessusSentence.subst(descr, ". The scanner");
descr = nessusTag.subst(descr, "> The scanner");
descr = nessusInside.subst(descr, "the scanner");
// Change semicolons into line breaks
// descr = semicolonMulti.subst(descr, "<br /><br />");
// Erase single semicolons
// descr = semicolonSingle.subst(descr, " ");
descr = semicolonSingle.subst(descr, "<br />");
// Put periods before each line break
/*
* if (noPeriod.match(descr)) { descr = noPeriod.subst(descr,
* noPeriod.getParen(1) + ". <br /> "); }
*/
// Remove any trailing break tags
descr = tooManyBreaks.subst(descr, "<br /><br />");
descr = leadingOrTrailingBreaks.subst(descr, "");
// Add an emphasized Risk Factor message
if (risk != null) {
descr = descr + "<br /><br />Risk Factor: <b>" + risk + "</b>";
}
// Add an emphasized Risk Factor message
if (retval.cveEntry != null) {
descr = descr + "<br /><br />Corresponding CVE Entry: <b>" + retval.cveEntry + "</b>";
}
// Replace the HTML tag tokens with HTML entities
descr = greaterThanToken.subst(descr, GREATER_THAN_ENTITY);
descr = lessThanToken.subst(descr, LESS_THAN_ENTITY);
// Set the return value's description
retval.descr = descr.trim();
if (retval.isValid())
return retval;
else
throw new IllegalArgumentException("String could not be parsed into a DescrValues object");
}
/**
* <p>getDefaultDescrValues</p>
*
* @return a {@link org.opennms.netmgt.vulnscand.DescrValues} object.
*/
public static DescrValues getDefaultDescrValues() {
DescrValues retval = new DescrValues();
retval.useDefaults();
return retval;
}
/**
* <p>getDefaultPortValues</p>
*
* @return a {@link org.opennms.netmgt.vulnscand.PortValues} object.
*/
public static PortValues getDefaultPortValues() {
PortValues retval = new PortValues();
retval.useDefaults();
return retval;
}
private ThreadCategory log() {
return ThreadCategory.getInstance(getClass());
}
}