Suricata (EVE) Analyst
Instructions
Step 1: Set Up the Environment First
Always initialize a local Python environment with uv before reading or transforming EVE logs.
- Create the virtual environment:
uv venv
source .venv/bin/activate
- Install the required libraries:
uv pip install polars orjson
- Create reusable scripts in the current working directory. Do not rely on ad hoc shell one-liners for repeatable analysis.
Step 2: Initial Discovery with orjson
Use orjson for fast line-by-line inspection and schema sampling before building larger polars workflows.
- Sample the data: Create a script such as
sample_eve.py to inspect a few records without loading the full file.import orjson
with open("eve.json", "rb") as f:
for _ in range(5):
line = f.readline()
if not line:
break
print(orjson.dumps(orjson.loads(line), option=orjson.OPT_INDENT_2).decode())
- Count event types: Create a script such as
count_events.py to see which protocol records are available.import orjson
from collections import Counter
counts = Counter()
with open("eve.json", "rb") as f:
for line in f:
event = orjson.loads(line)
counts[event.get("event_type", "unknown")] += 1
for event_type, count in counts.most_common():
print(f"{count:7} {event_type}")
- Consult references: Use
references/eve_format.md for common fields and references/suricata_eve_analysis.md for Polars-based hunting patterns.
Step 3: Targeted Analysis with polars
- Check for existing Parquet files: Before scanning
eve.json, check the current directory for .parquet files (e.g., dns.parquet, flow.parquet). If they exist, use polars.scan_parquet() for significantly faster analysis.
- Filter noise early: If starting from
eve.json, exclude stats events and prioritize alert, dns, tls, http, flow, and quic.
- Use lazy scans for scale: Prefer
polars.scan_ndjson() so large eve.json files are processed lazily instead of loaded eagerly.
- Persist JSON data as Parquet: Materialize filtered or flattened datasets to Parquet early (e.g.,
df.sink_parquet("dns.parquet")) so repeated analysis does not require rescanning raw JSON.
- Flatten only what you need: Select the few nested fields relevant to the hypothesis being tested, then collect just that subset.
- Document findings: Maintain an
analyst_log-YY-MM-DD_HH-MM.md file for every session.
Working Agreements
- Python environment: ALWAYS create a virtual environment with
uv venv and install dependencies with uv pip install polars orjson. Do NOT use uv run.
- Tool re-use: ALWAYS search for and re-use existing tools and scripts in the current directory before creating new ones.
- Data-First retrieval: ALWAYS check for and use existing
.parquet files in the current directory before rescanning eve.json.
- Script retention: Always create and retain scripts such as
analyze_*.py in the current project directory. Do not place analysis scripts in /tmp and NEVER delete generated helper scripts or analysis code.
- Data persistence: Persist intermediate or normalized EVE datasets to Parquet with
polars (e.g., sink_parquet) when the analysis will require repeated filtering, grouping, or joins.
- Timestamping: Rename throwaway notes or scratch markdown files with a
-YY-MM-DD_HH-MM.md suffix.
- Python style: Prefer
orjson for streaming JSON parsing and polars for filtering, aggregations, joins, and exports.
Examples
Example 1: Hunting for Rare SNIs
User says: "Check for suspicious TLS connections."
Action:
- Filter to
event_type == "tls" with polars.scan_ndjson().
- Extract
tls.sni and count occurrences.
- Highlight rare or unique SNIs and correlate them with
src_ip, dest_ip, and JA3 values.
Example 2: Volume-based Exfiltration
User says: "Find any hosts sending large amounts of data to the internet."
Action:
- Filter to
event_type == "flow".
- Persist the filtered flow dataset to Parquet for repeatable analysis.
- Sum
flow.bytes_toserver by src_ip for external destinations.
- Calculate directional imbalance and flag hosts with high upload volume and repeated external connections.
Troubleshooting
Error: "Invalid JSON" or "Line Truncated"
Cause: The EVE log may have been cut off during collection or copy.
Solution: Use an orjson script that catches decode errors, reports the bad line number, and continues parsing valid records.
Error: "Polars schema mismatch" or missing nested fields
Cause: EVE records are sparse and different event_type values expose different nested structures.
Solution: Filter by event_type first, then select nested fields with null-tolerant expressions instead of assuming every record shares the same schema.
Error: "Repeated scans of eve.json are too slow"
Cause: Large NDJSON inputs are being re-read for every aggregation or join.
Solution: Persist the normalized subset to Parquet with polars and rerun iterative analysis against the Parquet file instead of the original JSON.
Error: "No alerts found"
Cause: The log may only contain metadata, or Suricata signatures did not trigger.
Solution: Pivot to protocol-based hunting in DNS, TLS, HTTP, and flow records using references/suricata_eve_analysis.md.
1---2name: suricata-analyst3description: Analyzes Suricata EVE JSON logs to identify network threats, suspicious egress, and protocol anomalies. Use when a user provides eve.json logs, asks for network traffic analysis, or needs to hunt for C2 beaconing and data exfiltration.4---56# Suricata (EVE) Analyst78## Instructions910### Step 1: Set Up the Environment First1112Always initialize a local Python environment with `uv` before reading or transforming EVE logs.13141. **Create the virtual environment**:15 ```bash16 uv venv17 source .venv/bin/activate18 ```192. **Install the required libraries**:20 ```bash21 uv pip install polars orjson22 ```233. **Create reusable scripts in the current working directory**. Do not rely on ad hoc shell one-liners for repeatable analysis.2425### Step 2: Initial Discovery with `orjson`2627Use `orjson` for fast line-by-line inspection and schema sampling before building larger `polars` workflows.28291. **Sample the data**: Create a script such as `sample_eve.py` to inspect a few records without loading the full file.30 ```python31 import orjson3233 with open("eve.json", "rb") as f:34 for _ in range(5):35 line = f.readline()36 if not line:37 break38 print(orjson.dumps(orjson.loads(line), option=orjson.OPT_INDENT_2).decode())39 ```402. **Count event types**: Create a script such as `count_events.py` to see which protocol records are available.41 ```python42 import orjson43 from collections import Counter4445 counts = Counter()46 with open("eve.json", "rb") as f:47 for line in f:48 event = orjson.loads(line)49 counts[event.get("event_type", "unknown")] += 15051 for event_type, count in counts.most_common():52 print(f"{count:7} {event_type}")53 ```543. **Consult references**: Use `references/eve_format.md` for common fields and `references/suricata_eve_analysis.md` for Polars-based hunting patterns.5556### Step 3: Targeted Analysis with `polars`57581. **Check for existing Parquet files**: Before scanning `eve.json`, check the current directory for `.parquet` files (e.g., `dns.parquet`, `flow.parquet`). If they exist, use `polars.scan_parquet()` for significantly faster analysis.592. **Filter noise early**: If starting from `eve.json`, exclude `stats` events and prioritize `alert`, `dns`, `tls`, `http`, `flow`, and `quic`.603. **Use lazy scans for scale**: Prefer `polars.scan_ndjson()` so large `eve.json` files are processed lazily instead of loaded eagerly.614. **Persist JSON data as Parquet**: Materialize filtered or flattened datasets to Parquet early (e.g., `df.sink_parquet("dns.parquet")`) so repeated analysis does not require rescanning raw JSON.625. **Flatten only what you need**: Select the few nested fields relevant to the hypothesis being tested, then collect just that subset.636. **Document findings**: Maintain an `analyst_log-YY-MM-DD_HH-MM.md` file for every session.6465## Working Agreements66- **Python environment**: ALWAYS create a virtual environment with `uv venv` and install dependencies with `uv pip install polars orjson`. Do NOT use `uv run`.67- **Tool re-use**: ALWAYS search for and re-use existing tools and scripts in the current directory before creating new ones.68- **Data-First retrieval**: ALWAYS check for and use existing `.parquet` files in the current directory before rescanning `eve.json`.69- **Script retention**: Always create and retain scripts such as `analyze_*.py` in the current project directory. Do not place analysis scripts in `/tmp` and **NEVER** delete generated helper scripts or analysis code.70- **Data persistence**: Persist intermediate or normalized EVE datasets to Parquet with `polars` (e.g., `sink_parquet`) when the analysis will require repeated filtering, grouping, or joins.71- **Timestamping**: Rename throwaway notes or scratch markdown files with a `-YY-MM-DD_HH-MM.md` suffix.72- **Python style**: Prefer `orjson` for streaming JSON parsing and `polars` for filtering, aggregations, joins, and exports.7374## Examples7576### Example 1: Hunting for Rare SNIs77**User says**: "Check for suspicious TLS connections."78**Action**:791. Filter to `event_type == "tls"` with `polars.scan_ndjson()`.802. Extract `tls.sni` and count occurrences.813. Highlight rare or unique SNIs and correlate them with `src_ip`, `dest_ip`, and JA3 values.8283### Example 2: Volume-based Exfiltration84**User says**: "Find any hosts sending large amounts of data to the internet."85**Action**:861. Filter to `event_type == "flow"`.872. Persist the filtered flow dataset to Parquet for repeatable analysis.883. Sum `flow.bytes_toserver` by `src_ip` for external destinations.894. Calculate directional imbalance and flag hosts with high upload volume and repeated external connections.9091## Troubleshooting9293### Error: "Invalid JSON" or "Line Truncated"94**Cause**: The EVE log may have been cut off during collection or copy.95**Solution**: Use an `orjson` script that catches decode errors, reports the bad line number, and continues parsing valid records.9697### Error: "Polars schema mismatch" or missing nested fields98**Cause**: EVE records are sparse and different `event_type` values expose different nested structures.99**Solution**: Filter by `event_type` first, then select nested fields with null-tolerant expressions instead of assuming every record shares the same schema.100101### Error: "Repeated scans of eve.json are too slow"102**Cause**: Large NDJSON inputs are being re-read for every aggregation or join.103**Solution**: Persist the normalized subset to Parquet with `polars` and rerun iterative analysis against the Parquet file instead of the original JSON.104105### Error: "No alerts found"106**Cause**: The log may only contain metadata, or Suricata signatures did not trigger.107**Solution**: Pivot to protocol-based hunting in DNS, TLS, HTTP, and flow records using `references/suricata_eve_analysis.md`.