METRON-157 Create CEF Parser (simonellistonball via kylerichardson) closes 
apache/incubator-metron#451


Project: http://git-wip-us.apache.org/repos/asf/incubator-metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metron/commit/9e15cb6e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metron/tree/9e15cb6e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metron/diff/9e15cb6e

Branch: refs/heads/Metron_0.3.1
Commit: 9e15cb6e24872620ec4bf3c183d15dd6292b153d
Parents: 246acff
Author: simonellistonball <si...@simonellistonball.com>
Authored: Tue Feb 21 15:50:51 2017 -0500
Committer: Kyle Richardson <kylerichard...@apache.org>
Committed: Tue Feb 21 15:50:51 2017 -0500

----------------------------------------------------------------------
 metron-platform/metron-parsers/pom.xml          |   2 +-
 .../apache/metron/parsers/cef/CEFParser.java    | 274 ++++++++++++++++++
 .../apache/metron/parsers/utils/DateUtils.java  | 115 ++++++++
 .../metron/parsers/cef/CEFParserTest.java       | 277 +++++++++++++++++++
 .../org/apache/metron/parsers/cef/adallom.cef   |   1 +
 .../apache/metron/parsers/cef/adallom.schema    |  37 +++
 .../org/apache/metron/parsers/cef/cyberark.cef  |   1 +
 .../org/apache/metron/parsers/cef/cyberark.json |  21 ++
 .../apache/metron/parsers/cef/cyberark.schema   |  38 +++
 .../org/apache/metron/parsers/cef/waf.cef       |   1 +
 .../org/apache/metron/parsers/cef/waf.schema    |  67 +++++
 11 files changed, 833 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/pom.xml
----------------------------------------------------------------------
diff --git a/metron-platform/metron-parsers/pom.xml 
b/metron-platform/metron-parsers/pom.xml
index d8a77a0..3049a71 100644
--- a/metron-platform/metron-parsers/pom.xml
+++ b/metron-platform/metron-parsers/pom.xml
@@ -143,7 +143,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>${global_hbase_guava_version}</version>
+            <version>${global_guava_version}</version>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/cef/CEFParser.java
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/cef/CEFParser.java
 
b/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/cef/CEFParser.java
new file mode 100644
index 0000000..a765dd8
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/cef/CEFParser.java
@@ -0,0 +1,274 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.metron.parsers.cef;
+
+import java.nio.charset.Charset;
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.metron.parsers.BasicParser;
+import org.apache.metron.parsers.ParseException;
+import org.apache.metron.parsers.utils.DateUtils;
+import org.apache.metron.parsers.utils.SyslogUtils;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CEFParser extends BasicParser {
+       private static final long serialVersionUID = 1L;
+
+       protected static final Logger LOG = 
LoggerFactory.getLogger(CEFParser.class);
+       private static final String HEADER_CAPTURE_PATTERN = "[^\\|]*";
+       private static final String EXTENSION_CAPTURE_PATTERN = "(?<!\\\\)=";
+       private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+       private Pattern p;
+       private Pattern pext;
+
+       public void init() {
+
+               // CEF Headers: Device Vendor|Device Product|Device 
Version|Device Event
+               // Class ID|Name|Severity
+
+               String syslogTime = 
"(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\\b
 +(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) 
(?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9])?";
+               String syslogTime5424 = 
"(?:\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2}))";
+               String syslogPriority = "<(?:[0-9]+)>";
+               String syslogHost = "[a-z0-9\\.\\\\-_]+";
+
+               StringBuilder sb = new StringBuilder("(?<syslogTime>");
+               sb.append(syslogTime);
+               sb.append("|");
+               sb.append(syslogTime5424);
+               sb.append(")?");
+
+               sb.append("(?<syslogHost>");
+               sb.append(syslogHost);
+               sb.append(")?");
+
+               sb.append("(?<syslogPriority>");
+               sb.append(syslogPriority);
+               sb.append(")?");
+
+               sb.append(".*");
+
+               sb.append("CEF:0\\|");
+
+               headerBlock("DeviceVendor", sb);
+               sb.append("\\|");
+               headerBlock("DeviceProduct", sb);
+               sb.append("\\|");
+               headerBlock("DeviceVersion", sb);
+               sb.append("\\|");
+               headerBlock("DeviceEvent", sb);
+               sb.append("\\|");
+               headerBlock("Name", sb);
+               sb.append("\\|");
+               headerBlock("Severity", sb);
+               sb.append("\\|");
+
+               // extension capture:
+               sb.append("(?<extensions>.*)");
+               String pattern = sb.toString();
+
+               p = Pattern.compile(pattern);
+
+               // key finder for extensions
+               pext = Pattern.compile(EXTENSION_CAPTURE_PATTERN);
+       }
+
+       @SuppressWarnings("unchecked")
+       public List<JSONObject> parse(byte[] rawMessage) {
+               List<JSONObject> messages = new ArrayList<>();
+
+               String cefString = new String(rawMessage, UTF_8);
+
+               Matcher matcher = p.matcher(cefString);
+
+               while (matcher.find()) {
+                       JSONObject obj = new JSONObject();
+                       if (matcher.matches()) {
+                               LOG.info(String.format("Found %d groups", 
matcher.groupCount()));
+                               obj.put("DeviceVendor", 
matcher.group("DeviceVendor"));
+                               obj.put("DeviceProduct", 
matcher.group("DeviceProduct"));
+                               obj.put("DeviceVersion", 
matcher.group("DeviceVersion"));
+                               obj.put("DeviceEvent", 
matcher.group("DeviceEvent"));
+                               obj.put("Name", matcher.group("Name"));
+                               obj.put("Severity", 
standardizeSeverity(matcher.group("Severity")));
+                       }
+
+                       String ext = matcher.group("extensions");
+                       Matcher m = pext.matcher(ext);
+
+                       int index = 0;
+                       String key = null;
+                       String value = null;
+                       Map<String, String> labelMap = new HashMap<String, 
String>();
+
+                       while (m.find()) {
+                               if (key == null) {
+                                       key = ext.substring(index, m.start());
+                                       index = m.end();
+                                       if (!m.find()) {
+                                               break;
+                                       }
+                               }
+                               value = ext.substring(index, m.start());
+                               index = m.end();
+                               int v = value.lastIndexOf(" ");
+                               if (v > 0) {
+                                       String temp = value.substring(0, 
v).trim();
+                                       if (key.endsWith("Label")) {
+                                               labelMap.put(key.substring(0, 
key.length() - 5), temp);
+                                       } else {
+                                               obj.put(key, temp);
+                                       }
+                                       key = value.substring(v).trim();
+                               }
+                       }
+                       value = ext.substring(index);
+
+                       // Build a map of Label extensions to apply later
+                       if (key.endsWith("Label")) {
+                               labelMap.put(key.substring(0, key.length() - 
5), value);
+                       } else {
+                               obj.put(key, value);
+                       }
+
+                       // Apply the labels to custom fields
+                       for (Entry<String, String> label : labelMap.entrySet()) 
{
+                               mutate(obj, label.getKey(), label.getValue());
+                       }
+
+                       // Rename standard CEF fields to comply with Metron 
standards
+                       obj = mutate(obj, "dst", "ip_dst_addr");
+                       obj = mutate(obj, "dpt", "ip_dst_port");
+                       obj = convertToInt(obj, "ip_dst_port");
+
+                       obj = mutate(obj, "src", "ip_src_addr");
+                       obj = mutate(obj, "spt", "ip_src_port");
+                       obj = convertToInt(obj, "ip_src_port");
+
+                       obj = mutate(obj, "act", "deviceAction");
+                       // applicationProtocol
+                       obj = mutate(obj, "app", "protocol");
+
+                       obj.put("original_string", cefString);
+
+                       // apply timestamp from message if present, using rt, 
syslog
+                       // timestamp,
+                       // default to current system time
+
+                       if (obj.containsKey("rt")) {
+                               String rt = (String) obj.get("rt");
+                               try {
+                                       obj.put("timestamp", 
DateUtils.parseMultiformat(rt, DateUtils.DATE_FORMATS_CEF));
+                               } catch (java.text.ParseException e) {
+                                       throw new IllegalStateException("rt 
field present in CEF but cannot be parsed", e);
+                               }
+                       } else {
+                               String logTimestamp = 
matcher.group("syslogTime");
+                               if (!(logTimestamp == null || 
logTimestamp.isEmpty())) {
+                                       try {
+                                               obj.put("timestamp", 
SyslogUtils.parseTimestampToEpochMillis(logTimestamp, Clock.systemUTC()));
+                                       } catch (ParseException e) {
+                                               throw new 
IllegalStateException("Cannot parse syslog timestamp", e);
+                                       }
+                               } else {
+                                       obj.put("timestamp", 
System.currentTimeMillis());
+                               }
+                       }
+
+                       // add the host
+                       String host = matcher.group("syslogHost");
+                       if (!(host == null || host.isEmpty())) {
+                               obj.put("host", host);
+                       }
+
+                       messages.add(obj);
+               }
+               return messages;
+       }
+
+       @SuppressWarnings("unchecked")
+       private JSONObject convertToInt(JSONObject obj, String key) {
+               if (obj.containsKey(key)) {
+                       obj.put(key, Integer.valueOf((String) obj.get(key)));
+               }
+               return obj;
+       }
+
+       private void headerBlock(String name, StringBuilder sb) {
+               
sb.append("(?<").append(name).append(">").append(HEADER_CAPTURE_PATTERN).append(")");
+       }
+
+       /**
+        * Maps string based severity in CEF format to integer.
+        * 
+        * The strings are mapped according to the CEF 23 specification, taking 
the
+        * integer value as the value of the range buckets rounded up
+        * 
+        * The valid string values are: Unknown, Low, Medium, High, and 
Very-High.
+        * The valid integer values are: 0-3=Low, 4-6=Medium, 7- 8=High, and
+        * 9-10=Very-High.
+        * 
+        * @param severity
+        *            String or Integer
+        * @return Integer value mapped from the string
+        */
+       private Integer standardizeSeverity(String severity) {
+               if (severity.length() < 3) {
+                       // should be a number
+                       return Integer.valueOf(severity);
+               } else {
+                       switch (severity) {
+                       case "Low":
+                               return 2;
+                       case "Medium":
+                               return 5;
+                       case "High":
+                               return 8;
+                       case "Very-High":
+                               return 10;
+                       default:
+                               return 0;
+                       }
+               }
+       }
+
+       @Override
+       public void configure(Map<String, Object> config) {
+               // TODO Auto-generated method stub
+
+       }
+
+       @SuppressWarnings("unchecked")
+       private JSONObject mutate(JSONObject json, String oldKey, String 
newKey) {
+               if (json.containsKey(oldKey)) {
+                       json.put(newKey, json.remove(oldKey));
+               }
+               return json;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/utils/DateUtils.java
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/utils/DateUtils.java
 
b/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/utils/DateUtils.java
new file mode 100644
index 0000000..888649a
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/main/java/org/apache/metron/parsers/utils/DateUtils.java
@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.metron.parsers.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Various utilities for parsing and extracting dates
+ * 
+ */
+public class DateUtils {
+
+       public static List<SimpleDateFormat> DATE_FORMATS_CEF = new 
ArrayList<SimpleDateFormat>() {
+               {
+                       // as per CEF Spec
+                       add(new SimpleDateFormat("MMM dd HH:mm:ss.SSS zzz"));
+                       add(new SimpleDateFormat("MMM dd HH:mm:ss.SSS"));
+                       add(new SimpleDateFormat("MMM dd HH:mm:ss zzz"));
+                       add(new SimpleDateFormat("MMM dd HH:mm:ss"));
+                       add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS 
zzz"));
+                       add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS"));
+                       add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss zzz"));
+                       add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss"));
+                       // found in the wild
+                       add(new SimpleDateFormat("dd MMMM yyyy HH:mm:ss"));
+               }
+       };
+
+       public static List<SimpleDateFormat> DATE_FORMATS_SYSLOG = new 
ArrayList<SimpleDateFormat>() {
+               {
+                       // As specified in https://tools.ietf.org/html/rfc5424
+                       add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
+
+                       // common format per rsyslog defaults e.g. Mar 21 
14:05:02
+                       add(new SimpleDateFormat("MMM dd HH:mm:ss"));
+                       add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss"));
+
+                       // additional formats found in the wild
+                       add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
+                       add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
+                       add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
+
+               }
+       };
+
+       Pattern NUMERIC = Pattern.compile("\\b\\d+\\b");
+
+       /**
+        * Parse the data according to a sequence of possible parse patterns.
+        * 
+        * If the given date is entirely numeric, it is assumed to be a unix
+        * timestamp.
+        * 
+        * If the year is not specified in the date string, use the current 
year.
+        * Assume that any date more than 4 days in the future is in the past 
as per
+        * SyslogUtils
+        * 
+        * @param candidate
+        *            The possible date.
+        * @param validPatterns
+        *            A list of SimpleDateFormat instances to try parsing with.
+        * @return A java.util.Date based on the parse result
+        * @throws ParseException
+        */
+       public static long parseMultiformat(String candidate, 
List<SimpleDateFormat> validPatterns) throws ParseException {
+               if (StringUtils.isNumeric(candidate)) {
+                       return Long.valueOf(candidate);
+               } else {
+                       for (SimpleDateFormat pattern : validPatterns) {
+                               try {
+                                       Calendar cal = Calendar.getInstance();
+                                       cal.setTime(pattern.parse(candidate));
+                                       Calendar current = 
Calendar.getInstance();
+                                       if (cal.get(Calendar.YEAR) == 1970) {
+                                               cal.set(Calendar.YEAR, 
current.get(Calendar.YEAR));
+                                       }
+                                       current.add(Calendar.DAY_OF_MONTH, 4);
+                                       if (cal.after(current)) {
+                                               cal.add(Calendar.YEAR, -1);
+                                       }
+                                       return cal.getTimeInMillis();
+                               } catch (ParseException e) {
+                                       continue;
+                               }
+                       }
+                       throw new ParseException("Failed to parse any of the 
given date formats", 0);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
 
b/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
new file mode 100644
index 0000000..88c0f0c
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
@@ -0,0 +1,277 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.metron.parsers.cef;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.fge.jackson.JsonLoader;
+import com.github.fge.jsonschema.core.report.ProcessingReport;
+import com.github.fge.jsonschema.main.JsonSchemaFactory;
+import com.github.fge.jsonschema.main.JsonValidator;
+import com.google.common.io.Resources;
+
+import junit.framework.TestCase;
+
+public class CEFParserTest extends TestCase {
+
+       private static final Charset UTF_8 = Charset.forName("utf-8");
+       private CEFParser parser;
+
+       @Override
+       public void setUp() {
+               parser = new CEFParser();
+               parser.init();
+       }
+
+       @Test
+       public void testInvalid() {
+               List<JSONObject> obj = parse("test test test nonsense\n");
+               assertEquals(0, obj.size());
+       }
+
+       @Test
+       public void testEscaping() {
+               for (JSONObject obj : parse(
+                               "Sep 19 08:26:10 host 
CEF:0|security|threatmanager|1.0|100|detected a \\ in packet|10|src=10.0.0.1 
act=blocked a \\ dst=1.1.1.1")) {
+                       assertEquals("10.0.0.1", obj.get("ip_src_addr"));
+                       assertEquals("blocked a \\", obj.get("deviceAction"));
+                       assertEquals("1.1.1.1", obj.get("ip_dst_addr"));
+               }
+       }
+
+       public void testBasicHeader() {
+               for (JSONObject obj : parse(
+                               "CEF:0|Security|threatmanager|1.0|100|worm 
successfully stopped|10|src=10.0.0.1 dst=2.1.2.2 spt=1232")) {
+                       assertEquals("Security", obj.get("DeviceVendor"));
+                       assertEquals("threatmanager", obj.get("DeviceProduct"));
+                       assertEquals("1.0", obj.get("DeviceVersion"));
+                       assertEquals("100", obj.get("DeviceEvent"));
+                       assertEquals("worm successfully stopped", 
obj.get("Name"));
+                       assertEquals(10, obj.get("Severity"));
+               }
+       }
+
+       public void testBasicExtensions() {
+               for (JSONObject obj : parse(
+                               "CEF:0|Security|threatmanager|1.0|100|worm 
successfully stopped|10|src=10.0.0.1 dst=2.1.2.2 spt=1232")) {
+                       assertEquals("10.0.0.1", obj.get("ip_src_addr"));
+                       assertEquals("2.1.2.2", obj.get("ip_dst_addr"));
+                       assertEquals(1232, obj.get("ip_src_port"));
+               }
+       }
+
+       public void testCustomLabelWithSpace() {
+               for (JSONObject obj : parse(
+                               "CEF:0|Security|threatmanager|1.0|100|worm 
successfully stopped|10|src=10.0.0.1 dst=2.1.2.2 spt=1232 custom=Text with 
space customLabel=Label with space")) {
+                       assertEquals(true, obj.containsKey("Label with space"));
+                       assertEquals("Text with space", obj.get("Label with 
space"));
+               }
+       }
+
+       public void testTimestampPriority() throws java.text.ParseException {
+               long correctTime = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz").parse("2016-05-01T09:29:11.356-0400")
+                               .getTime();
+
+               SimpleDateFormat sdf = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz");
+
+               for (JSONObject obj : parse(
+                               "CEF:0|Security|threatmanager|1.0|100|worm 
successfully stopped|10|src=10.0.0.1 rt=May 1 2016 09:29:11.356 -0400 
dst=2.1.2.2 spt=1232")) {
+                       assertEquals(new Date(correctTime), new Date((long) 
obj.get("timestamp")));
+                       assertEquals(correctTime, obj.get("timestamp"));
+               }
+               for (JSONObject obj : parse(
+                               "2016-06-01T09:29:11.356-04:00 host 
CEF:0|Security|threatmanager|1.0|100|worm successfully stopped|10|src=10.0.0.1 
rt=May 1 2016 09:29:11.356 -0400 dst=2.1.2.2 spt=1232")) {
+                       assertEquals(new Date(correctTime), new Date((long) 
obj.get("timestamp")));
+                       assertEquals(correctTime, obj.get("timestamp"));
+               }
+               for (JSONObject obj : parse(
+                               "2016-05-01T09:29:11.356-04:00 host 
CEF:0|Security|threatmanager|1.0|100|worm successfully stopped|10|src=10.0.0.1 
dst=2.1.2.2 spt=1232")) {
+                       assertEquals(new Date(correctTime), new Date((long) 
obj.get("timestamp")));
+                       assertEquals(correctTime, obj.get("timestamp"));
+               }
+               for (JSONObject obj : parse(
+                               "CEF:0|Security|threatmanager|1.0|100|worm 
successfully stopped|10|src=10.0.0.1 dst=2.1.2.2 spt=1232")) {
+                       assertNotNull(obj.get("timestamp"));
+               }
+
+       }
+
+       public void testRtValueAsEpochTimestamp() throws 
java.text.ParseException {
+               long correctTime = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz").parse("2016-05-01T09:29:11.356-0400")
+                               .getTime();
+               for (JSONObject obj : 
parse("CEF:0|Security|threatmanager|1.0|100|worm successfully 
stopped|10|src=10.0.0.1 rt="
+                               + String.valueOf(correctTime) + " dst=2.1.2.2 
spt=1232")) {
+                       assertEquals(new Date(correctTime), new Date((long) 
obj.get("timestamp")));
+                       assertEquals(correctTime, obj.get("timestamp"));
+               }
+       }
+
+       private void runMissingYear(Calendar expected, Calendar input) {
+               SimpleDateFormat sdf = new SimpleDateFormat("MMM dd 
HH:mm:ss.SSS");
+               for (JSONObject obj : 
parse("CEF:0|Security|threatmanager|1.0|100|worm successfully 
stopped|10|src=10.0.0.1 rt="
+                               + sdf.format(input.getTime()) + " dst=2.1.2.2 
spt=1232")) {
+                       assertEquals(expected.getTimeInMillis(), 
obj.get("timestamp"));
+                       assertEquals(expected.getTime(), new Date((long) 
obj.get("timestamp")));
+               }
+       }
+
+       public void testMissingYearFromDate() throws java.text.ParseException {
+               Calendar current = Calendar.getInstance();
+               Calendar correct = Calendar.getInstance();
+
+               correct.setTimeInMillis(current.getTimeInMillis());
+
+               runMissingYear(correct, current);
+       }
+
+       public void testFourDayFutureBecomesPast() {
+               Calendar current = Calendar.getInstance();
+               Calendar correct = Calendar.getInstance();
+
+               current.add(Calendar.DAY_OF_MONTH, 5);
+               // correct.setTime(current.getTime());
+               correct.setTimeInMillis(current.getTimeInMillis());
+               correct.add(Calendar.YEAR, -1);
+
+               runMissingYear(correct, current);
+       }
+
+       public void testCEFParserAdallom() throws Exception {
+               runTest("adallom", 
Resources.readLines(Resources.getResource(getClass(), "adallom.cef"), UTF_8),
+                               
Resources.toString(Resources.getResource(getClass(), "adallom.schema"), UTF_8));
+       }
+
+       public void testCEFParserCyberArk() throws Exception {
+               runTest("cyberark", 
Resources.readLines(Resources.getResource(getClass(), "cyberark.cef"), UTF_8),
+                               
Resources.toString(Resources.getResource(getClass(), "cyberark.schema"), UTF_8),
+                               
Resources.toString(Resources.getResource(getClass(), "cyberark.json"), UTF_8));
+       }
+
+       public void testCEFParserWAF() throws Exception {
+               URL waf_url = Resources.getResource(getClass(), "waf.cef");
+               runTest("waf", Resources.readLines(waf_url, UTF_8),
+                               
Resources.toString(Resources.getResource(getClass(), "waf.schema"), UTF_8));
+       }
+
+       private void runTest(String name, List<String> lines, String schema) 
throws Exception {
+               runTest(name, lines, schema, "");
+       }
+
+       private void runTest(String name, List<String> lines, String schema, 
String targetJson) throws Exception {
+               for (String inputString : lines) {
+                       JSONObject parsed = parse(inputString).get(0);
+                       assertNotNull(parsed);
+                       assertNotNull(parsed.get("timestamp"));
+                       assertTrue((long) parsed.get("timestamp") > 0);
+
+                       System.out.println(parsed);
+                       JSONParser parser = new JSONParser();
+
+                       Map<?, ?> json = null;
+                       try {
+                               json = (Map<?, ?>) 
parser.parse(parsed.toJSONString());
+                               Assert.assertEquals(true, 
validateJsonData(schema, json.toString()));
+                       } catch (ParseException e) {
+                               e.printStackTrace();
+                       }
+
+                       // test against an explicit json example
+                       if (!targetJson.isEmpty()) {
+
+                       }
+               }
+       }
+
+       /**
+        * Additional Sample from NiFi test Suite
+        * 
(https://github.com/apache/nifi/blob/rel/nifi-1.1.1/nifi-nar-bundles/nifi
+        * 
-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/
+        * processors/standard/TestParseCEF.java)
+        */
+       private final static String sample = 
"CEF:0|TestVendor|TestProduct|TestVersion|TestEventClassID|TestName|Low|" +
+                       // TimeStamp, String and Long
+                       "rt=Feb 09 2015 00:27:43 UTC cn3Label=Test Long 
cn3=9223372036854775807 " +
+                       // FloatPoint and MacAddress
+                       "cfp1=1.234 cfp1Label=Test FP Number 
smac=00:00:0c:07:ac:00 " +
+                       // IPv6 and String
+                       "c6a3=2001:cdba::3257:9652 c6a3Label=Test IPv6 
cs1Label=Test String cs1=test test test chocolate " +
+                       // IPv4
+                       "destinationTranslatedAddress=123.123.123.123 " +
+                       // Date without TZ
+                       "deviceCustomDate1=Feb 06 2015 13:27:43 " +
+                       // Integer and IP Address (from v4)
+                       "dpt=1234 agt=123.123.0.124 dlat=40.366633 " +
+                       // A JSON object inside one of CEF's custom Strings
+                       "cs2Label=JSON payload "
+                       + "cs2={\"test_test_test\": \"chocolate!\", 
\"what?!?\": \"Simple! test test test chocolate!\"}";
+
+       @Test
+       public void testSuccessfulWhenCEFContainsJSON() throws 
JsonProcessingException, IOException {
+               List<JSONObject> parse = parse(sample);
+               JSONObject obj = parse.get(0);
+
+               assertEquals("TestVendor", obj.get("DeviceVendor"));
+               assertEquals(1423441663000L, obj.get("timestamp"));
+               assertEquals("9223372036854775807", obj.get("Test Long"));
+               assertEquals(obj.get("Test FP Number"), String.valueOf(1.234F));
+               assertEquals("00:00:0c:07:ac:00", obj.get("smac"));
+               assertEquals("2001:cdba::3257:9652", obj.get("Test IPv6"));
+               assertEquals("test test test chocolate", obj.get("Test 
String"));
+               assertEquals("123.123.123.123", 
obj.get("destinationTranslatedAddress"));
+
+               JsonNode inner = new ObjectMapper().readTree((String) 
obj.get("JSON payload"));
+               Assert.assertEquals("chocolate!", 
inner.get("test_test_test").asText());
+       }
+
+       protected boolean validateJsonData(final String jsonSchema, final 
String jsonData) throws Exception {
+               final JsonNode d = JsonLoader.fromString(jsonData);
+               final JsonNode s = JsonLoader.fromString(jsonSchema);
+
+               final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
+               JsonValidator v = factory.getValidator();
+
+               ProcessingReport report = v.validate(s, d);
+               System.out.println(report);
+
+               return report.toString().contains("success");
+       }
+
+       private List<JSONObject> parse(String string) {
+               List<JSONObject> parse = 
parser.parse(string.getBytes(Charset.forName("utf-8")));
+               assertNotNull(parse);
+               return parse;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.cef
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.cef
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.cef
new file mode 100644
index 0000000..a35f354
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.cef
@@ -0,0 +1 @@
+2016-04-01T09:29:11.356-0400 
CEF:0|Adallom|Adallom|1.0|56fe779ee4b0459f4e9a484a|ALERT_CABINET_EVENT_MATCH_AUDIT|0|msg=Activity
 policy 'User download/view file' was triggered by 'per...@example.com' 
suser=au...@example.com start=1459517280810 end=1459517280810 
audits=["AVPR-4oIPeFmuZ3CKKrg","AVPR-wx80cd9PUpAu2aj","AVPR-6XGPeFmuZ3CKKvx","AVPSALn_qE4Kgs_8_yK9","AVPSASW3gw_f3aEvgEmi"]
 services=["APPID_SXC"] users=["anot...@example.com"] 
cs6=https://abcd-remote.console.arc.com/#/alerts/56fe779ee4b0459f4e9a484a 
cs6Label=consoleUrl
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.schema
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.schema
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.schema
new file mode 100644
index 0000000..a91cce0
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/adallom.schema
@@ -0,0 +1,37 @@
+{
+       "title": "Adallom Schema",
+       "type": "object",
+       "properties": {
+               "original_string": {
+                       "type": "string"
+               },
+               "timestamp": {
+                       "type": "integer"
+               },
+               "DeviceVendor": {
+                       "type": "string"
+               },
+               "DeviceProduct": {
+                       "type": "string"
+               },
+               "DeviceVersion": {
+                       "type": "string"
+               },
+               "DeviceEvent": {
+                       "type": "string"
+               },
+               "Name": {
+                       "type": "string"
+               },
+               "Severity": {
+                       "type": "integer"
+               },
+               "consoleUrl": {
+                       "type": "string"
+               }
+       },
+       "required": [
+       "original_string", "timestamp", 
+       "DeviceVendor", "DeviceProduct", "DeviceVersion", "Name", "Severity",
+       "consoleUrl"]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.cef
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.cef
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.cef
new file mode 100644
index 0000000..9d4fe6f
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.cef
@@ -0,0 +1 @@
+Mar 21 14:05:02 HHHPVATN1 CEF:0|Cyber-Ark|Vault|7.20.0091|295|Retrieve 
password|5|act=Retrieve password suser=spilgrim fname=Root\ABC phobos3 - COMP 
dvc=120.99.70.3 shost=10.44.134.78 dhost= duser= externalId= app= reason= 
cs1Label="Affected User Name" cs1= cs2Label="Safe Name" cs2=Security 
Vulnerability Mgmt cs3Label="Device Type" cs3= cs4Label="Database" cs4= 
cs5Label="Other info" cs5=101.198.70.93 cn1Label="Request Id" cn1= 
cn2Label="Ticket Id" cn2=Needed to verify config files being pulled msg=Needed 
to verify config files being pulled
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.json
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.json
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.json
new file mode 100644
index 0000000..e900a9a
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.json
@@ -0,0 +1,21 @@
+{
+    "\"Other info\"": "101.198.70.93",
+    "\"Safe Name\"": "Security Vulnerability Mgmt",
+    "\"Ticket Id\"": "Needed to verify config files being pulled ",
+    "deviceAction": "Retrieve password",
+    "deviceAddress": "120.99.70.3",
+    "device_product": "Vault",
+    "device_vendor": "Cyber-Ark",
+    "device_version": "7.20.0091",
+    "event_class_id": "295",
+    "event_name": "Retrieve password",
+    "fileName": "Root\\ABC phobos3 - COMP",
+    "header": "Mar 21 14:05:02 HHHPVATN1 CEF:0",
+    "message": "Needed to verify config files being pulled",
+    "original_string": "Mar 21 14:05:02 HHHPVATN1 
CEF:0|Cyber-Ark|Vault|7.20.0091|295|Retrieve password|5|act=Retrieve password 
suser=spilgrim fname=Root\\ABC phobos3 - COMP dvc=120.99.70.3 
shost=10.44.134.78 dhost= duser= externalId= app= reason= cs1Label=\"Affected 
User Name\" cs1= cs2Label=\"Safe Name\" cs2=Security Vulnerability Mgmt 
cs3Label=\"Device Type\" cs3= cs4Label=\"Database\" cs4= cs5Label=\"Other 
info\" cs5=101.198.70.93 cn1Label=\"Request Id\" cn1= cn2Label=\"Ticket Id\" 
cn2=Needed to verify config files being pulled msg=Needed to verify config 
files being pulled",
+    "severity": "5",
+    "source.type": "cyberark",
+    "src_hostname": "10.44.134.78",
+    "src_username": "spilgrim",
+    "timestamp": 1458569102000
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.schema
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.schema
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.schema
new file mode 100644
index 0000000..5bd1021
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/cyberark.schema
@@ -0,0 +1,38 @@
+{
+       "title": "CyberArk Schema",
+       "type": "object",
+       "properties": {
+               "ip_src_addr": {
+                       "type": "string"
+               },
+               "ip_dst_addr": {
+                       "type": "string"
+               },
+               "original_string": {
+                       "type": "string"
+               },
+               "timestamp": {
+                       "type": "integer"
+               },
+               "DeviceVendor": {
+                       "type": "string"
+               },
+               "DeviceProduct": {
+                       "type": "string"
+               },
+               "DeviceVersion": {
+                       "type": "string"
+               },
+               "DeviceEvent": {
+                       "type": "string"
+               },
+               "Name": {
+                       "type": "string"
+               },
+               "Severity": {
+                       "type": "integer"
+               }
+       },
+       "required": ["original_string", "timestamp", 
+       "DeviceVendor", "DeviceProduct", "DeviceVersion", "Name", "Severity"]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.cef
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.cef
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.cef
new file mode 100644
index 0000000..86e1d6b
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.cef
@@ -0,0 +1 @@
+<14>CEF:0|Imperva Inc.|SecureSphere|10.0.0.4_16|ABC - Secure Login.vm Page 
Rate Limit UK - Source IP||High|act=alert dst=17.43.200.42 dpt=88 
duser=${Alert.username} src=10.31.45.69 spt=34435 proto=TCP rt=31 March 2016 
13:04:55 cat=Alert cs1= cs1Label=Policy cs2=ABC-Secure cs2Label=ServerGroup 
cs3=servers_svc cs3Label=ServiceName cs4=server_app cs4Label=ApplicationName 
cs5=QA cs5Label=Description
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/9e15cb6e/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.schema
----------------------------------------------------------------------
diff --git 
a/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.schema
 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.schema
new file mode 100644
index 0000000..b38485c
--- /dev/null
+++ 
b/metron-platform/metron-parsers/src/test/resources/org/apache/metron/parsers/cef/waf.schema
@@ -0,0 +1,67 @@
+{
+       "title": "WAF CEF Schema",
+       "type": "object",
+       "properties": {
+               "ip_src_addr": {
+                       "type": "string"
+               },
+               "ip_src_port": {
+                       "type": "integer"
+               },
+               "ip_dst_addr": {
+                       "type": "string"
+               },
+               "ip_dst_port": {
+                       "type": "integer"
+               },
+               "original_string": {
+                       "type": "string"
+               },
+               "@version": {
+                       "type": "string"
+               },
+               "timestamp": {
+                       "type": "integer"
+               },
+               "type": {
+                       "type": "string"
+               },
+               "DeviceVendor": {
+                       "type": "string"
+               },
+               "DeviceProduct": {
+                       "type": "string"
+               },
+               "DeviceVersion": {
+                       "type": "string"
+               },
+               "DeviceEvent": {
+                       "type": "string"
+               },
+               "Name": {
+                       "type": "string"
+               },
+               "Severity": {
+                       "type": "integer"
+               },
+               "cat": {
+                       "type": "string"
+               },
+               "ServerGroup": {
+                       "type": "string"
+               },
+               "ServiceName": {
+                       "type": "string"
+               },
+               "ApplicationName": {
+                       "type": "string"
+               },
+               "Description": {
+                       "type": "string"
+               }
+       },
+       "required": ["ip_src_addr", "ip_dst_addr", "ip_src_port", 
"ip_dst_port", "original_string", "timestamp", 
+               "DeviceVendor", "DeviceProduct", "DeviceVersion", "Name", 
"Severity",  
+               "cat", 
+               "ServerGroup", "ServiceName", "ApplicationName", "Description"]
+}
\ No newline at end of file


Reply via email to