# Sibling decoder: plain-text audit lines from the same internal portal.
#
# Demonstrates parse|<field> with logpar expressions instead of a JSON helper.
# It produces the same Wazuh Common Schema fields as decoder/portal-audit-json/0,
# so one detection rule covers both log formats.
#
# Logpar syntax used here (all documented):
#   <field>            schema field, parser chosen from the schema type
#   <_name>            temporary variable, stripped before indexing
#   literal text       matched exactly; a field needs a literal after it as an
#                      end token unless its parser does not require one
#   several expressions under one parse| are tried in order, first match wins
# Source: https://github.com/wazuh/wazuh/tree/main/docs/ref/modules/engine (Parse stage)
#         https://github.com/wazuh/wazuh/blob/main/docs/guide/migration/xml-decoders-migration.md
#
# Sample input line (see sample-events.log):
# 2026-09-18T09:20:55.004Z portal-audit login.failed user=r.navarro role=admin
# src=198.51.100.23:44120 method=POST path=/session status=401 reason=bad_password
---
name: "decoder/portal-audit-text/0"
id: "6ef353cb-dc24-4947-a455-5e8611c3b2eb"
enabled: true
parents:
- "decoder/core-wazuh-message/0"
metadata:
  title: "Internal portal plain-text audit log"
  description: "Normalises the legacy plain-text audit line written by the internal\
    \ portal application into the same Wazuh Common Schema fields as the JSON decoder."
  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: "contains($event.original, portal-audit)"
parse|event.original:
- "<_ts> portal-audit <_evt> user=<user.name> role=<_role> src=<source.ip>:<source.port>\
  \ method=<http.request.method> path=<url.path> status=<http.response.status_code>\
  \ reason=<event.reason>"
- "<_ts> portal-audit <_evt> user=<user.name> role=<_role> src=<source.ip>:<source.port>\
  \ method=<http.request.method> path=<url.path> status=<http.response.status_code>"
normalize:
- map:
  - event.kind: "event"
  - event.dataset: "portal-audit"
  - event.module: "portal-audit"
  - event.category: "array_append(authentication)"
  - event.start: "parse_date($_ts, ISO8601Z)"
  - user.roles: "array_append($_role)"
  - source.address: "$source.ip"
  - related.ip: "array_append($source.ip)"
  - related.user: "array_append($user.name)"

- check: "$_evt == login.failed"
  map:
  - event.action: "authentication-failure"
  - event.outcome: "failure"
  - event.type: "array_append(info)"

- check: "$_evt == login.success"
  map:
  - event.action: "logged-in"
  - event.category: "array_append(session)"
  - event.outcome: "success"
  - event.type: "array_append(start)"

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