com.denimgroup.threadfix.service.waflog.RiverbedWebAppFirewallLogParser.java Source code

Java tutorial

Introduction

Here is the source code for com.denimgroup.threadfix.service.waflog.RiverbedWebAppFirewallLogParser.java

Source

////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2015 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
//
//     Software distributed under the License is distributed on an "AS IS"
//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//     License for the specific language governing rights and limitations
//     under the License.
//
//     The Original Code is ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2013 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
//
//     Software distributed under the License is distributed on an "AS IS"
//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//     License for the specific language governing rights and limitations
//     under the License.
//
//     The Original Code is ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s): Riverbed Technology
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.service.waflog;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.io.StringReader;
import java.io.IOException;

import org.apache.commons.csv.CSVRecord;
import org.apache.commons.csv.CSVFormat;

import com.denimgroup.threadfix.data.dao.SecurityEventDao;
import com.denimgroup.threadfix.data.dao.WafRuleDao;
import com.denimgroup.threadfix.data.entities.SecurityEvent;
import com.denimgroup.threadfix.data.entities.WafRule;
import com.denimgroup.threadfix.logging.SanitizedLogger;

/**
 * @author: Mirko Dziadzka, Riverbed Technology
 *
 */
public class RiverbedWebAppFirewallLogParser extends WafLogParser {

    protected final SanitizedLogger log = new SanitizedLogger(this.getClass());
    private static final String THREADFIX_HANDLER_NAME = "ThreadfixHandler";
    private static final String THREADFIX_HANDLER_COMPONENT = "protection_rules";
    private static final String LOG_TIMESTAMP_FORMAT = "yyyyMMdd-HHmmss"; // 20140131-172342
    private static final String WAF_LOG_MODE_PROTECTION = "P";
    private static final String WAF_LOG_MODE_DETECTION = "D";

    /**
     * @param wafRuleDao
     * @param securityEventDao
     */
    public RiverbedWebAppFirewallLogParser(WafRuleDao wafRuleDao, SecurityEventDao securityEventDao) {
        this.wafRuleDao = wafRuleDao;
        this.securityEventDao = securityEventDao;
    }

    /**
     * @param entryBuffer
     * @return
     */
    @Override
    public SecurityEvent getSecurityEvent(String entry) {
        if (entry == null || entry.isEmpty() || entry.startsWith("#")) {
            return null;
        }

        // a logline is a csv encoded line with the following columns
        //  * [0] a timestamp: YYYYMMDD-HHMMSS in local time
        //  * [1] an internal session id or "default"
        //  * [2] internal cluster node id
        //  * [3] host header
        //  * [4] client ip
        //  * [5] HTTP method
        //  * [6] URL
        //  * [7] HTTP protocol version
        //  * [8] internal ruleset / rule id
        //  * [9] action
        //  * [10] protection or detection mode
        //  * [11] request or response
        //  * [12] handlerName - we only care for the THREADFIX_HANDLER_NAME here
        //  * [13] component which reject the request
        //  * [14] value which rejects the request
        //  * [16] error id (use this together with the timetamp to be unique)
        //  * [17] free text field
        //  * ... aditional stuff

        try {
            // we are using an iterator here because this
            // is the interface of this CSV parser 
            // however, we always feed only one line into
            // this parser so it is ok to return from this
            // loop and never continue
            Iterable<CSVRecord> parser = CSVFormat.DEFAULT.parse(new StringReader(entry));
            for (CSVRecord record : parser) {

                // We access elements 0 .. 17 later, so this has to have at least 18 elements
                if (record.size() < 18) {
                    log.error("can't parse logline: " + entry);
                    return null;
                }
                String csvTimestamp = record.get(0); // 20140131-172342
                String csvClientIP = record.get(4); // 10.17.23.41
                String csvRulesetMode = record.get(10); // P or D
                String csvHandlerName = record.get(12); // ThreadfixHandler
                String csvComponentName = record.get(13); // protection_ruleset
                String csvComponentValue = record.get(14); // threadfix:100042 or 100042
                String csvErrorId = record.get(16); // 1234567
                String csvFreeText = record.get(17); // free text which describe the action

                if (csvTimestamp == null || csvClientIP == null || csvHandlerName == null || csvRulesetMode == null
                        || csvComponentName == null || csvComponentValue == null || csvErrorId == null
                        || csvFreeText == null) {

                    log.error("can't parse logline: " + entry);
                    return null;
                }

                // we only care for THREADFIX_HANDLER_NAME here ... ignore all other stuff
                if (!csvHandlerName.equals(THREADFIX_HANDLER_NAME)) {
                    log.debug("ignore unknown handler: " + csvHandlerName);
                    return null;
                }

                // while the error id act more or less as
                // a unique id for rejected requests, this id
                // is too short to be really unique over a
                // long time. So we combine it here with the
                // timestamp to get a better native id
                String nativeId = csvTimestamp + "-" + csvErrorId;

                log.debug("native id: " + nativeId);

                if (securityEventDao.retrieveByNativeIdAndWafId(nativeId, wafId) != null) {
                    return null;
                }

                String wafRuleId = null;
                if (csvComponentName.equals(THREADFIX_HANDLER_COMPONENT)) {
                    // allow threadfix:123456 and 123456
                    if (csvComponentValue.contains(":")) {
                        wafRuleId = csvComponentValue.split(":", 2)[1];
                    } else {
                        wafRuleId = csvComponentValue;
                    }
                } else {
                    log.debug("ignore unknown component: " + csvComponentName);
                    return null;
                }

                log.debug("wafRuleId " + wafRuleId);

                WafRule rule = wafRuleDao.retrieveByWafAndNativeId(wafId, wafRuleId);
                if (rule == null) {
                    log.debug("wafRule not found");
                    return null;
                }

                Calendar calendar = parseDate(csvTimestamp);

                if (calendar == null) {
                    log.error("can't parse logline (timestamp): " + entry);
                    return null;
                }

                SecurityEvent event = new SecurityEvent();

                event.setWafRule(rule);
                event.setImportTime(calendar);
                event.setLogText(csvFreeText);

                event.setAttackType("deny");
                //if (csvRulesetMode == WAF_LOG_MODE_PROTECTION)
                //{
                //    event.setAttackType("deny");
                //} else {
                //    event.setAttackType("log"); 
                //}
                event.setNativeId(nativeId);
                event.setAttackerIP(csvClientIP);

                return event;
            }
        } catch (IOException e) {
            return null;
        }
        return null;

    }

    public static Calendar parseDate(String time) {
        SimpleDateFormat formatter = new SimpleDateFormat(LOG_TIMESTAMP_FORMAT);
        Date date = null;

        if (time == null) {
            return null;
        }

        try {
            date = formatter.parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        if (date == null) {
            return null;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        return calendar;
    }
}