diff --git a/README.md b/README.md index 7e50bd2..190486c 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,23 @@ ![GitHub Action Status](https://github.com/elnappo/bro-log-parser/workflows/Python%20package/badge.svg) [![Maintainability](https://api.codeclimate.com/v1/badges/680163011be7d7903c0f/maintainability)](https://codeclimate.com/github/elnappo/bro-log-parser/maintainability) -Simple logfile parser for [Bro IDS](https://www.bro.org/). This library parses and transforms entries +Simple logfile parser for [Bro IDS](https://www.bro.org/). This library parses and transforms entries in a logfile created by the [ASCII Writer](https://www.bro.org/sphinx/frameworks/logging.html#ascii-writer) into a dynamically generated namedtuple. Fields are converted into native Python data types. +Additionally, it provides a command-line tool named `catz` that makes it easier to work with Zeek log files manually. + ## Requirements * python3 - + ## Install python3 setup.py install +## Usage of the CLI + ./catz conn.log + +![](https://user-images.githubusercontent.com/5559994/102645090-35ca8f80-4162-11eb-9be6-ec2a2eec06c1.png) + ## Tests pytest # OR @@ -28,7 +35,7 @@ into a dynamically generated namedtuple. Fields are converted into native Python ... ConnEntry( ts=datetime.datetime(2015, 1, 23, 0, 49, 13, 396481), - uid='CjPbcf1SkE86OWWTra', + uid='CjPbcf1SkE86OWWTra', id_orig_h=IPv4Address('192.168.1.100'), id_orig_p=137, id_resp_h=IPv4Address('192.168.1.255'), diff --git a/catz b/catz new file mode 100755 index 0000000..65547aa --- /dev/null +++ b/catz @@ -0,0 +1,2 @@ +#!/bin/sh +exec python3 "$(dirname "$(readlink -f "$0")")"/catz.py "$@" 2>&1 | less -Snc diff --git a/catz.py b/catz.py new file mode 100755 index 0000000..044e063 --- /dev/null +++ b/catz.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +import sys +from brologparse import parse_log_file + +if len(sys.argv) < 2: + print("Log parser for Bro/Zeek logfiles") + print("Usage: catz ") + sys.exit(1) + +entries = [] +for entry in parse_log_file(sys.argv[1]): + entries.append(entry) + +fields = [] +with open(sys.argv[1], "r") as f: + separator = f.readline().rstrip("\n").split(" ")[1].encode('raw_unicode_escape').decode('unicode_escape') + for i in range(5): + f.readline() + fields = [field.replace(".", "_") for field in f.readline().rstrip("\n").lstrip("#").split(separator)[1:]] + +formatted_entries = [] +field_lengths = {} +for field in fields: + field_lengths[field] = len(field) +for entry in entries: + formatted_entry = [] + for field in fields: + formatted_field = "" + unformatted_field = getattr(entry, field) + if unformatted_field is not None: + formatted_field = "{}".format(unformatted_field) + formatted_entry.append(formatted_field) + if len(formatted_field) > field_lengths[field]: + field_lengths[field] = len(formatted_field) + formatted_entries.append(formatted_entry) +for i, field in enumerate(fields): + if field_lengths[field] > 80: + field_lengths[field] = 80 + for entry in formatted_entries: + if len(entry[i]) > field_lengths[field]: + entry[i] = entry[i][:field_lengths[field] - 1] + "…" + +row_format = "{:>" + "{}".format(len("{}".format(len(formatted_entries)))) + "} | " +separator_line = [] +for field in fields: + row_format += "{:<" + "{}".format(field_lengths[field]) + "} " + separator_line.append("-" * field_lengths[field]) +print(row_format.format("", *fields)) +print(row_format.format("", *separator_line)) +i = 1 +for entry in formatted_entries: + print(row_format.format(i, *entry)) + i += 1