Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
<antlr.version>4.6</antlr.version>
<jackson.version>2.9.2</jackson.version>
<junit.version>5.0.2</junit.version>
<mockito.version>2.12.0</mockito.version>
<mockito.version>2.28.2</mockito.version>

<gpg.keyname>B1606F22</gpg.keyname>
<maven.deploy.skip>true</maven.deploy.skip>
Expand Down
78 changes: 14 additions & 64 deletions src/main/java/com/github/jcustenborder/cef/CEFParserImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,33 +37,19 @@ class CEFParserImpl implements CEFParser {
private static final Logger log = LoggerFactory.getLogger(CEFParserImpl.class);
private static final Pattern PATTERN_CEF_PREFIX = Pattern.compile("^((?<timestamp>.+)\\s+(?<host>\\S+)\\s+).*?(?<cs0>CEF:\\d+)|^.*?(?<cs1>CEF:\\d+)");
private static final Pattern PATTERN_OSSEC_PREFIX = Pattern.compile("^(?<timestamp>[A-Za-z]+\\s+\\d{1,2}\\s+\\d{1,2}:\\d{2}:\\d{2})\\s+(?:ASM:)?(?<cs>CEF:\\d+)");

private static final Pattern PATTERN_CEF_MAIN = Pattern.compile("(?<!\\\\)\\|");
private static final Pattern PATTERN_EXTENSION = Pattern.compile("(\\w+)=");
private static final List<String> DATE_FORMATS = Arrays.asList(
"MMM dd yyyy HH:mm:ss.SSS zzz",
"MMM dd yyyy HH:mm:ss.SSS",
"MMM dd yyyy HH:mm:ss zzz",
"MMM dd yyyy HH:mm:ss",
"MMM dd HH:mm:ss.SSS zzz",
"MMM dd HH:mm:ss.SSS",
"MMM dd HH:mm:ss zzz",
"MMM dd HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSS zzz",
"yyyy-MM-dd'T'HH:mm:ss zzz",
"yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ",
"yyyy-MM-dd'T'HH:mm:ss ZZZ",
"yyyy-MM-dd'T'HH:mm:ss.SSS XXX",
"yyyy-MM-dd'T'HH:mm:ss XXX",
"yyyy-MM-dd'T'HH:mm:ssXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
);

final MessageFactory messageFactory;
private DateParser dateParser;

public CEFParserImpl(MessageFactory messageFactory) {
this(messageFactory, new DateParserImpl());
}

public CEFParserImpl(MessageFactory messageFactory, DateParser dateParser) {
this.messageFactory = messageFactory;
this.dateParser = dateParser;
}

@Override
Expand Down Expand Up @@ -98,8 +84,8 @@ public Message parse(final String event, final TimeZone timeZone, final Locale l

final int cefstartIndex;
if (isOssec) {
final String df = "MMM dd HH:mm:ss";
final SimpleDateFormat dateFormat = new SimpleDateFormat(df);
final String dateFormatPattern = "MMM dd HH:mm:ss";
final SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatPattern);
dateFormat.setTimeZone(timeZone);
final Date timestamp;
try {
Expand All @@ -110,7 +96,7 @@ public Message parse(final String event, final TimeZone timeZone, final Locale l
calendar.set(Calendar.YEAR, thisYear);
timestamp = calendar.getTime();
} catch (ParseException e) {
log.trace("parse() - Could not parse '{}' with '{}'.", timestampText, df);
log.trace("parse() - Could not parse '{}' with '{}'.", timestampText, dateFormatPattern);
throw new IllegalStateException("Could not parse timestamp. '" + timestampText + "'");
}

Expand All @@ -119,49 +105,13 @@ public Message parse(final String event, final TimeZone timeZone, final Locale l

cefstartIndex = ossecPrefixMatcher.start("cs");
} else if (timestampText != null && !timestampText.isEmpty() && host != null && !host.isEmpty()) {
Long longTimestamp = null;
final Date timestamp;
try {
longTimestamp = Long.parseLong(timestampText);
} catch (NumberFormatException e) {
log.trace("Unable to parse timestamp '{}'", timestampText);
timestamp = this.dateParser.parseDate(timestampText, timeZone, locale);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(e);
}
Date timestamp = null;
if (null != longTimestamp) {
log.trace("parse() - Detected timestamp is stored as a long.");
timestamp = new Date(longTimestamp);
} else {
log.trace("parse() - Trying to parse the timestamp.");
// SimpleDateFormat is not threadsafe so we have to create them each time.
for (String df : DATE_FORMATS) {
SimpleDateFormat dateFormat = new SimpleDateFormat(df, locale);
dateFormat.setTimeZone(timeZone);
try {
log.trace("parse() - Trying to parse '{}' with format '{}'", timestampText, df);
timestamp = dateFormat.parse(timestampText);
final boolean alterYear = !df.contains("yyyy");

if (alterYear) {
log.trace("parse() - date format '{}' does not specify the year. Might need to alter the year.", df);
Calendar calendar = Calendar.getInstance(timeZone);
int thisYear = calendar.get(Calendar.YEAR);
calendar.setTime(timestamp);
final int year = calendar.get(Calendar.YEAR);
if (1970 == year) {
log.trace("parse() - altering year from {} to {}", year, thisYear);
calendar.set(Calendar.YEAR, thisYear);
timestamp = calendar.getTime();
}
}

break;
} catch (ParseException e) {
log.trace("parse() - Could not parse '{}' with '{}'.", timestampText, df);
}
}
if (null == timestamp) {
throw new IllegalStateException("Could not parse timestamp. '" + timestampText + "'");
}
}
log.trace("parse() - timestamp = {}, {}", timestamp.getTime(), timestamp);
builder.timestamp(timestamp);
builder.host(host);
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/github/jcustenborder/cef/DateParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright © 2017 Jeremy Custenborder ([email protected])
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.github.jcustenborder.cef;

import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
* This interface attempts to parse a string containing a date and time.
*/
public interface DateParser {

/**
* Attempts to parse supplied <code>dateTime</code> string.
* @param timestampText Text representing a valid timestamp
* @param timeZone A valid time zone.
* @param locale The parsed date
* @return
* @throws IllegalArgumentException
*/
Date parseDate(final String timestampText, final TimeZone timeZone, final Locale locale);

}
97 changes: 97 additions & 0 deletions src/main/java/com/github/jcustenborder/cef/DateParserImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Copyright © 2017 Jeremy Custenborder ([email protected])
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.github.jcustenborder.cef;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DateParserImpl implements DateParser {
private static final List<String> DATE_FORMATS = Arrays.asList(
"MMM dd yyyy HH:mm:ss.SSS zzz",
"MMM dd yyyy HH:mm:ss.SSS",
"MMM dd yyyy HH:mm:ss zzz",
"MMM dd yyyy HH:mm:ss",
"MMM dd HH:mm:ss.SSS zzz",
"MMM dd HH:mm:ss.SSS",
"MMM dd HH:mm:ss zzz",
"MMM dd HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSS zzz",
"yyyy-MM-dd'T'HH:mm:ss zzz",
"yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ",
"yyyy-MM-dd'T'HH:mm:ss ZZZ",
"yyyy-MM-dd'T'HH:mm:ss.SSS XXX",
"yyyy-MM-dd'T'HH:mm:ss XXX",
"yyyy-MM-dd'T'HH:mm:ssXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
);
private static final Logger log = LoggerFactory.getLogger(DateParserImpl.class);

@Override
public Date parseDate(String timestampText, final TimeZone timeZone, final Locale locale) {
try {
Long longTimestamp = Long.parseLong(timestampText);
log.trace("parse() - Detected timestamp is stored as a long.");

return new Date(longTimestamp);
} catch (NumberFormatException e) {
log.trace("Unable to parse timestamp '{}'", timestampText);
}

log.trace("parse() - Trying to parse the timestamp.");
// SimpleDateFormat is not threadsafe so we have to create them each time.
for (String pattern : DATE_FORMATS) {
SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, locale);
dateFormat.setTimeZone(timeZone);
try {
log.trace("parse() - Trying to parse '{}' with format '{}'", timestampText, pattern);
Date timestamp = dateFormat.parse(timestampText);
final boolean alterYear = !pattern.contains("yyyy");

if (alterYear) {
log.trace("parse() - date format '{}' does not specify the year. Might need to alter the year.", pattern);
Calendar calendar = Calendar.getInstance(timeZone);
int thisYear = calendar.get(Calendar.YEAR);
calendar.setTime(timestamp);
final int year = calendar.get(Calendar.YEAR);
if (1970 == year) {
log.trace("parse() - altering year from {} to {}", year, thisYear);
calendar.set(Calendar.YEAR, thisYear);

return calendar.getTime();
}
}

return timestamp;
} catch (ParseException e) {
log.trace("parse() - Could not parse '{}' with '{}'.", timestampText, pattern);
}
}

throw new IllegalArgumentException("Could not parse timestamp. '" + timestampText + "'");
}
}
10 changes: 10 additions & 0 deletions src/test/java/com/github/jcustenborder/cef/MessageAssertions.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.util.Date;

public class MessageAssertions {
public static void assertMessage(Message expected, Message actual) {
if (null == expected) {
Expand All @@ -27,6 +29,14 @@ public static void assertMessage(Message expected, Message actual) {
assertNotNull(actual, "actual should not be null.");
}

// Force actual year to match the year that the tests were recorded
final Date expectedTimestamp = expected.timestamp(), actualTimestamp = actual.timestamp();
if (expectedTimestamp != null && actualTimestamp != null
&& expectedTimestamp.getYear() != actualTimestamp.getYear()) {
assertEquals(actualTimestamp.getYear(), new Date().getYear(), "year does not match");
actual.timestamp().setYear(expected.timestamp().getYear());
}

assertEquals(expected.timestamp(), actual.timestamp(), "timestamp() does not match");
assertEquals(expected.host(), actual.host(), "host() does not match");
assertEquals(expected.cefVersion(), actual.cefVersion(), "cefVersion() does not match");
Expand Down