Skip to content

Repository files navigation

LogNorm

Log Source Normalizer — Sysmon / WEL / Wazuh / syslog / CEF → ECS-lite schema

Part of the Nebula Forge detection engineering platform. LogNorm is the normalization gateway for the pipeline: its ECS-lite JSON output is the shared data currency accepted by DriftWatch, ClusterIQ, AtomicLoop, and HuntForge.

versionportpythonframeworkpart%20oflicense

What it does

LogNorm ingests raw log files from five source types and normalizes every record to a consistent ECS-lite JSON schema. Normalized events are stored in SQLite and can be exported as JSON or CSV. The Flask web UI provides an upload → normalize → view → export workflow; the CLI handles file-based automation.


Pipeline Position

Nebula Forge pipeline — LogNorm highlighted

purple-loop:AtomicLoop → LogNorm → ClusterIQ → DriftWatch → HuntForge → repeat


Screenshots

Dashboard

LogNorm Dashboard

Upload

LogNorm Upload


Source adapters

KeySourceInput formatCoverage
sysmonMicrosoft SysmonXML (wevtutil export)EventIDs 1–29 (process, network, file, registry, DNS, WMI, pipes)
welWindows Event LogCSV (Get-WinEvent / EndpointTriage Security.csv)4624/4625 logon, 4688 process, 4698 tasks, 4720/4726 accounts, 4740 lockout, 7045 service
wazuhWazuh alertsNDJSON / JSON array / single objectAll rule-based alerts; Windows data.win.*, syscheck, audit blocks
syslogLinux auth.log / syslogText (RFC 3164 / RFC 5424 / journald)SSH, sudo, PAM, cron, useradd, su, systemd
cefCEF / Generic JSON`CEF:0...

Quick start

# Install
pip install flask pyyaml
# Copy config
cp config.example.yaml config.yaml
# Run web app (default port 5006)
python app.py
# Or specify port
python app.py --port 5006

Open http://127.0.0.1:5006


Docker (Nebula Forge suite)

This tool runs as a containerized service in the Nebula Forge suite. The recommended way to start everything together:

# From the Nebula-Forge repo root
cp .env.example .env # add secrets (NVD_API_KEY, ATOMICLOOP_API_KEY, POSTGRES_PASSWORD — all required)
docker compose up -d # starts all services including lognorm

Access:http://localhost:5006

Standalone container:

docker build -t lognorm .
docker run -p 5006:5006 \
-e DATABASE_URL=postgresql://nebula:changeme@localhost:5432/nebula_forge \
lognorm

CLI

# Normalize Sysmon XML to JSON (stdout)
python cli.py --input sysmon.xml --source sysmon --pretty
# Normalize WEL CSV to output file
python cli.py --input security.csv --source wel --output normalized.json
# Normalize Wazuh alerts to CSV
python cli.py --input alerts.json --source wazuh --output out.csv --format csv
# Normalize auth.log (strip original_log to reduce size)
python cli.py --input /var/log/auth.log --source syslog --no-original-log
# Read from stdin
cat sysmon.xml | python cli.py --source sysmon --stdin
# All options
python cli.py --help

API endpoints

MethodPathDescription
GET/api/healthHealth check — {"status":"ok","tool":"lognorm","version":"1.0.0"}
GET/api/sourcesList supported source types and descriptions
POST/api/normalizeNormalize single record (JSON body)
POST/api/normalize/batchNormalize file (multipart) or record list (JSON)
GET/api/recordsList stored events (pagination + filters)
GET/api/record/<id>Fetch single stored event by UUID
GET/api/sessionsList normalization sessions
GET/api/exportExport as JSON or CSV (`?format=json
DELETE/api/recordsClear all stored records

POST /api/normalize

POST /api/normalize
{"source_type": "sysmon", "raw": "<Event>...</Event>"}
→ {"success": true, "event": {<ECS-lite>}, "session_id": "uuid"}

POST /api/normalize/batch

// JSON bodyPOST /api/normalize/batch
{"source_type": "wazuh", "raw": "...full file content..."}
{"source_type": "syslog", "records": ["Apr 3 ...", "Apr 3 ..."]}
// File upload (multipart/form-data)POST /api/normalize/batchfile=<binary> source_type=sysmon→ {"success": true, "events": [{<ECS-lite>}, ...], "failed": 0,"total": 45, "session_id": "uuid", "filename": "sysmon.xml"}

ECS-lite schema reference

Schema version: 1.0 — Required fields are event.id, event.created, event.source_type, log.source_type. All other fields are optional and omitted when empty.

Top-level

FieldTypeRequiredDescription
schema_versionstringyesAlways "1.0"
tagsarrayyesFree-form tags (e.g. T1059.001, atomic-test)

event

FieldTypeRequiredDescription
event.idstringyesUUID v4 — unique identifier for this normalized event
event.createdISO8601yesTimestamp the event was created (UTC)
event.source_typestringyesAdapter: sysmon | wel | wazuh | syslog | cef
event.categoryarraynoECS category: process | network | file | registry | authentication | iam | driver
event.typearraynoECS type: start | end | creation | deletion | access | change | connection | protocol
event.actionstringnoHuman-readable action from the source log
event.outcomestringnosuccess | failure | unknown
event.severityintegernoNormalized 0–100 severity
event.original_event_idstringnoEventID from source (e.g. 1, 4624)

host

FieldTypeRequiredDescription
host.namestringnoHostname of the originating endpoint
host.hostnamestringnoSame as host.name
host.iparraynoIP addresses associated with the host
host.os.typestringnowindows | linux | macos
host.os.namestringnoFull OS name

process

FieldTypeRequiredDescription
process.pidintegernoProcess ID
process.ppidintegernoParent process ID
process.namestringnoImage name (e.g. powershell.exe)
process.executablestringnoFull path to executable
process.command_linestringnoFull command line including arguments
process.hash.md5stringnoMD5 of process image (lowercase hex)
process.hash.sha256stringnoSHA-256 of process image (lowercase hex)
process.parent.pidintegernoParent PID
process.parent.namestringnoParent image name
process.parent.executablestringnoFull path to parent executable
process.parent.command_linestringnoParent command line

network

FieldTypeRequiredDescription
network.directionstringnoingress | egress | internal
network.transportstringnotcp | udp | icmp
network.destination.ipstringnoDestination IP
network.destination.portintegernoDestination port
network.destination.domainstringnoDestination hostname / domain
network.source.ipstringnoSource IP
network.source.portintegernoSource port

file

FieldTypeRequiredDescription
file.pathstringnoFull file path
file.namestringnoFilename without directory
file.extensionstringnoExtension without leading dot
file.hash.md5stringnoMD5 (lowercase hex)
file.hash.sha256stringnoSHA-256 (lowercase hex)
file.sizeintegernoSize in bytes

registry

FieldTypeRequiredDescription
registry.pathstringnoFull registry path
registry.keystringnoRegistry key name
registry.value.namestringnoValue name
registry.value.typestringnoValue type (REG_SZ, REG_DWORD, …)
registry.value.datastringnoValue data

user

FieldTypeRequiredDescription
user.namestringnoAccount username
user.domainstringnoDomain or workgroup
user.idstringnoSID or UID

log

FieldTypeRequiredDescription
log.source_typestringyesAdapter used
log.source_toolstringnoTool that generated the original log
log.original_event_idstringnoOriginal event identifier
log.original_logobjectnoRaw source record preserved verbatim

Nebula-Forge integration

nebula-dashboard config.yaml — add to the tools: block:

lognorm:
label: "LogNorm"url: "http://127.0.0.1:5006"health_path: "/api/health"description: "Log source normalizer — Sysmon / WEL / Wazuh / syslog / CEF → ECS-lite"category: "Normalize"

detection-pipeline / purple-loop config.yaml:

lognorm_url: "http://127.0.0.1:5006"

Architecture

LogNorm/
├── app.py Flask web app + all API endpoints
├── cli.py Standalone CLI
├── config.example.yaml Configuration template
├── adapters/
│ ├── base.py BaseAdapter — parse() + parse_file()
│ ├── sysmon.py Sysmon XML (EventIDs 1–29)
│ ├── wel.py Windows Event Log CSV
│ ├── wazuh.py Wazuh JSON / NDJSON
│ ├── syslog.py Linux auth.log / syslog / journald
│ └── cef.py CEF + generic JSON
├── core/
│ ├── engine.py NormalizationEngine — dispatch + fallback
│ ├── models.py make_ecs_event() factory + helpers
│ ├── schema.py Field reference + source descriptions
│ └── storage.py SQLite — sessions + events tables
├── static/css/style.css Nebula Forge dark theme
├── static/js/main.js Upload, normalize, render, pagination
└── templates/
├── base.html Sidebar layout
├── index.html Upload + normalize UI
├── records.html Browse stored events
└── schema.html ECS-lite field reference

Fallback pattern (consistent with detection-pipeline / ir-chain): if the SQLite write fails, the normalized events are saved to ./output/fallback_<source>_<timestamp>_<session>.json so no data is lost.


Home lab

Validated against:

  • Wazuh 4.14.4 at <wazuh-host> — Wazuh adapter targets this alert format
  • Windows Agent — Sysmon (SwiftOnSecurity config) + EndpointTriage Security.csv
  • Linux Agent — auth.log / journald output
  • Splunk on Linux Agent — WEL CSV exports

License

This project is licensed under the MIT License — see the LICENSE file for details.

Built by Rootless-Ghost

Part of the Nebula Forge security tools suite.

About

Log Source Normalizer — Sysmon / WEL / Wazuh / syslog / CEF → ECS-lite schema | Part of Nebula Forge

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages