# Child decoder: JSON audit lines from an internal portal application.
#
# Parent: decoder/core-wazuh-message/0 (the root decoder attached to the
# integration). The root decoder has already run parse_json($event.original)
# into the temporary field _tmp_json, so this decoder reads _tmp_json.
#
# Every key, stage and helper used here appears in the Wazuh 5.0-beta
# documentation. Sources:
#   - Decoder stages (check / parse|<field> / normalize with check+parse|+map):
#     https://github.com/wazuh/wazuh/tree/main/docs/ref/modules/engine
#   - parents, check expressions, array_append, delete(), parse_date(.., ISO8601Z):
#     https://documentation.wazuh.com/5.0-beta/user-manual/data-analysis/key-value-databases.html
#   - Decoder concepts and normalize blocks:
#     https://documentation.wazuh.com/5.0-beta/user-manual/data-analysis/decoders.html
#
# Sample input line (see sample-events.log):
# {"ts":"2026-09-18T09:14:07.412Z","app":"portal-audit","event":"login.failed",
#  "reason":"bad_password","user":"j.okafor","role":"admin","src_ip":"203.0.113.44",
#  "src_port":51544,"method":"POST","path":"/session","status":401,"session":"c6f0f0b1"}
---
name: "decoder/portal-audit-json/0"
id: "ac0e314b-0e57-45e6-9ec4-b013b66c090a"
enabled: true
parents:
- "decoder/core-wazuh-message/0"
metadata:
  title: "Internal portal JSON audit log"
  description: "Normalises the JSON audit log written by an internal portal application\
    \ into Wazuh Common Schema authentication fields."
  author: "Security engineering"
  date: "2026-09-22"
  modified: "2026-09-22"
  documentation: ""
  references:
  - "https://documentation.wazuh.com/5.0-beta/user-manual/data-analysis/decoders.html"
  supports: []
check: "$_tmp_json.app == portal-audit AND exists($_tmp_json.event)"
normalize:
# Block 1: unconditional base fields. A map-only block always runs.
- map:
  - event.kind: "event"
  - event.dataset: "portal-audit"
  - event.module: "portal-audit"
  - event.category: "array_append(authentication)"
  - event.start: "parse_date($_tmp_json.ts, ISO8601Z)"
  - user.name: "$_tmp_json.user"
  - user.roles: "array_append($_tmp_json.role)"
  - source.ip: "$_tmp_json.src_ip"
  - source.address: "$_tmp_json.src_ip"
  - source.port: "$_tmp_json.src_port"
  - http.request.method: "$_tmp_json.method"
  - http.response.status_code: "$_tmp_json.status"
  - url.path: "$_tmp_json.path"
  - related.ip: "array_append($source.ip)"
  - related.user: "array_append($user.name)"

# Block 2: failed authentication.
- check: "$_tmp_json.event == login.failed"
  map:
  - event.action: "authentication-failure"
  - event.outcome: "failure"
  - event.type: "array_append(info)"
  - event.reason: "$_tmp_json.reason"

# Block 3: successful authentication.
- check: "$_tmp_json.event == login.success"
  map:
  - event.action: "logged-in"
  - event.category: "array_append(session)"
  - event.outcome: "success"
  - event.type: "array_append(start)"

# Block 4: session end.
- check: "$_tmp_json.event == logout"
  map:
  - event.action: "logged-out"
  - event.category: "array_append(session)"
  - event.outcome: "success"
  - event.type: "array_append(end)"

# Block 5: drop the scratch field. Temporary variables (leading underscore) are
# removed automatically at pre-enrichment; deleting explicitly keeps later
# sibling decoders from reading a half-consumed object.
- map:
  - _tmp_json: "delete()"
