description: Pipeline for parsing sophos firewall logs (firewall pipeline)
processors:
#######################
## ECS Event Mapping ##
#######################
- set:
    field: event.kind
    value: event
- set:
    field: event.action
    value: "{{sophos.xg.log_subtype}}"
    if: "ctx.sophos?.xg?.log_subtype != null"
- set:
    field: event.outcome
    value: success
    if: "ctx.sophos?.xg?.log_subtype != null"
- set:
    field: event.kind
    value: alert
    if: '["03001", "05001", "05151", "00003", "00004"].contains(ctx.sophos?.xg?.message_id)'
- append:
    field: event.category
    value: intrusion_detection
    if: '["03001", "05001", "05151", "00003", "00004"].contains(ctx.sophos?.xg?.message_id)'
- append:
    field: event.category
    value: network
- append:
    field: event.type
    value:
      - start
      - allowed
      - connection
    if: "['Start', 'Interim'].contains(ctx.sophos?.xg?.connevent)"
- append:
    field: event.type
    value:
      - end
      - allowed
      - connection
    if: "ctx.sophos?.xg?.connevent == 'Stop'"
- append:
    field: event.type
    value:
      - denied
      - connection
    if: "ctx.sophos?.xg?.status == 'Deny'"
- rename:
    field: sophos.xg.log_id
    target_field: event.code
    ignore_missing: true
    if: "ctx.event?.code == null"

####################################
## ECS Server/Destination Mapping ##
####################################
- rename:
    field: sophos.xg.dst_ip
    target_field: destination.ip
    ignore_missing: true
    if: "ctx.sophos?.xg?.dst_ip != null"
- set:
    field: server.ip
    value: '{{destination.ip}}'
    if: "ctx.destination?.ip != null"
- rename:
    field: sophos.xg.tran_dst_ip
    target_field: destination.nat.ip
    ignore_missing: true
    if: "ctx.sophos?.xg?.tran_dst_ip != null"
- rename:
    field: sophos.xg.destinationip
    target_field: destination.ip
    ignore_missing: true
    if: "ctx.sophos?.xg?.destinationip !=null"
- convert:
    field: sophos.xg.dst_port
    target_field: destination.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.dst_port != null"
- set:
    field: server.port
    value: '{{destination.port}}'
    if: "ctx.destination?.port != null"
- convert:
    field: server.port
    target_field: server.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.server?.port != null"
- convert:
    field: sophos.xg.tran_dst_port
    target_field: destination.nat.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.tran_dst_port != null"
- set:
    field: server.nat.port
    value: '{{destination.nat.port}}'
    if: "ctx.destination?.nat?.port != null"
- convert:
    field: server.nat.port
    target_field: server.nat.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.server?.nat?.port != null"
- rename:
    field: sophos.xg.dst_mac
    target_field: destination.mac
    ignore_missing: true
    if: "ctx.sophos?.xg?.dst_mac != null"
- set:
    field: server.mac
    value: '{{destination.mac}}'
    if: "ctx.destination?.mac != null"
- convert:
    field: sophos.xg.recv_bytes
    target_field: destination.bytes
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.recv_bytes != null"
- set:
    field: server.bytes
    value: '{{destination.bytes}}'
    if: "ctx.destination?.bytes != null"
- convert:
    field: server.bytes
    target_field: server.bytes
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.server?.bytes != null"
- convert:
    field: sophos.xg.recv_pkts
    target_field: destination.packets
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.recv_pkts !=null"
- set:
    field: server.packets
    value: '{{destination.packets}}'
    if: "ctx.destination?.packets != null"
- convert:
    field: server.packets
    target_field: server.packets
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.server?.packets != null"

###############################
## ECS Client/Source Mapping ##
###############################
- rename:
    field: sophos.xg.src_ip
    target_field: source.ip
    ignore_missing: true
    if: "ctx.sophos?.xg?.src_ip != null"
- set:
    field: client.ip
    value: '{{source.ip}}'
    if: "ctx.source?.ip != null"
- rename:
    field: sophos.xg.tran_src_ip
    target_field: source.nat.ip
    ignore_missing: true
    if: "ctx.sophos?.xg?.tran_src_ip != null"
- rename:
    field: sophos.xg.sourceip
    target_field: source.ip
    ignore_missing: true
    if: "ctx.sophos?.xg?.sourceip != null"
- convert:
    field: sophos.xg.src_port
    target_field: source.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.src_port != null"
- set:
    field: client.port
    value: '{{source.port}}'
    if: "ctx.source?.port != null"
- convert:
    field: client.port
    target_field: client.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.client?.port != null"
- convert:
    field: sophos.xg.tran_src_port
    target_field: source.nat.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.tran_src_port != null"
- set:
    field: client.nat.port
    value: '{{source.nat.port}}'
    if: "ctx.source?.nat?.port != null"
- convert:
    field: client.nat.port
    target_field: client.nat.port
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.client?.nat?.port != null"
- rename:
    field: sophos.xg.src_mac
    target_field: source.mac
    ignore_missing: true
    if: "ctx.sophos?.xg?.src_mac != null"
- set:
    field: client.mac
    value: '{{source.mac}}'
    if: "ctx.source?.mac != null"
- convert:
    field: sophos.xg.sent_bytes
    target_field: source.bytes
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.sent_bytes != null"
- set:
    field: client.bytes
    value: '{{source.bytes}}'
    if: "ctx.source?.bytes != null"
- convert:
    field: client.bytes
    target_field: client.bytes
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.client?.bytes != null"
- trim:
    field: sophos.xg.sent_pkts
    ignore_missing: true
- convert:
    field: sophos.xg.sent_pkts
    target_field: source.packets
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.sophos?.xg?.sent_pkts != null"
- set:
    field: client.packets
    value: '{{source.packets}}'
    if: "ctx.source?.packets != null"
- convert:
    field: client.packets
    target_field: client.packets
    type: long
    ignore_failure: true
    ignore_missing: true
    if: "ctx.client?.packets != null"
- rename:
    field: sophos.xg.user_name
    target_field: source.user.name
    ignore_missing: true
    if: "ctx.sophos?.xg?.user_name != null"
- rename:
    field: sophos.xg.user_gp
    target_field: source.user.group.name
    ignore_missing: true
    if: "ctx.sophos?.xg?.user_gp != null"

######################
## ECS Rule Mapping ##
######################
- rename:
    field: sophos.xg.fw_rule_id
    target_field: rule.id
    ignore_missing: true
    if: "ctx.rule?.id == null"
- rename:
    field: sophos.xg.policy_type
    target_field: rule.ruleset
    ignore_missing: true
    if: "ctx.sophos?.xg?.policy_type != null"

#############################
## ECS Network/Geo Mapping ##
#############################
- rename:
    field: sophos.xg.application
    target_field: network.protocol
    ignore_missing: true
    if: "ctx.sophos?.xg?.application != null"
- rename:
    field: sophos.xg.protocol
    target_field: network.transport
    ignore_missing: true
    if: "ctx.sophos?.xg?.protocol != null"
- geoip:
    field: source.ip
    target_field: source.geo
    ignore_missing: true
    if: "ctx.source?.geo == null"
- geoip:
    field: destination.ip
    target_field: destination.geo
    ignore_missing: true
    if: "ctx.destination?.geo == null"
- geoip:
    database_file: GeoLite2-ASN.mmdb
    field: source.ip
    target_field: source.as
    properties:
    - asn
    - organization_name
    ignore_missing: true
- geoip:
    database_file: GeoLite2-ASN.mmdb
    field: destination.ip
    target_field: destination.as
    properties:
    - asn
    - organization_name
    ignore_missing: true
- geoip:
    field: source.nat.ip
    target_field: source.geo
    ignore_missing: true
    if: "ctx.source?.geo == null"
- geoip:
    field: destination.nat.ip
    target_field: destination.geo
    ignore_missing: true
    if: "ctx.destination?.geo == null"
- geoip:
    database_file: GeoLite2-ASN.mmdb
    field: source.nat.ip
    target_field: source.as
    properties:
    - asn
    - organization_name
    ignore_missing: true
    if: "ctx.source?.as == null"
- geoip:
    database_file: GeoLite2-ASN.mmdb
    field: destination.nat.ip
    target_field: destination.as
    properties:
    - asn
    - organization_name
    ignore_missing: true
    if: "ctx.destination?.as == null"
- rename:
    field: source.as.asn
    target_field: source.as.number
    ignore_missing: true
- rename:
    field: source.as.organization_name
    target_field: source.as.organization.name
    ignore_missing: true
- rename:
    field: destination.as.asn
    target_field: destination.as.number
    ignore_missing: true
- rename:
    field: destination.as.organization_name
    target_field: destination.as.organization.name
    ignore_missing: true
- script:
    lang: painless
    source: "ctx.network.bytes = ctx.source.bytes + ctx.destination.bytes"
    if: "ctx?.source?.bytes != null && ctx?.destination?.bytes != null"
    ignore_failure: true
- script:
    lang: painless
    source: "ctx.network.packets = ctx.source.packets + ctx.destination.packets"
    if: "ctx?.source?.packets != null && ctx?.destination?.packets != null"
    ignore_failure: true
- set:
    field: network.direction
    value: inbound
    if: "['LAN', 'DMZ', 'VPN', 'WiFi'].contains(ctx?.observer?.egress?.zone) && ctx?.observer?.ingress?.zone == 'WAN'"
- set:
    field: network.direction
    value: outbound
    if: "['LAN', 'DMZ', 'VPN', 'WiFi'].contains(ctx?.observer?.ingress?.zone) && ctx?.observer?.egress?.zone == 'WAN'"
- set:
    field: network.direction
    value: internal
    if: "['LAN', 'DMZ', 'VPN', 'WiFi'].contains(ctx?.observer?.ingress?.zone) && ['LAN', 'DMZ', 'VPN', 'WiFi'].contains(ctx?.observer?.egress?.zone)"
- set:
    field: network.direction
    value: external
    if: "ctx?.observer?.ingress?.zone == 'WAN' && ctx?.observer?.egress?.zone == 'WAN'"

#########################
## ECS Related Mapping ##
#########################
- append:
    field: related.ip
    value: '{{source.ip}}'
    allow_duplicates: false
    if: 'ctx?.source?.ip != null'
- append:
    field: related.ip
    value: '{{destination.ip}}'
    allow_duplicates: false
    if: 'ctx?.destination?.ip != null'
- append:
    field: related.ip
    value: '{{source.nat.ip}}'
    allow_duplicates: false
    if: 'ctx?.source?.nat?.ip != null'
- append:
    field: related.ip
    value: '{{destination.nat.ip}}'
    allow_duplicates: false
    if: 'ctx?.destination?.nat?.ip != null'
- append:
    field: related.user
    value: "{{source.user.name}}"
    allow_duplicates: false
    if: "ctx.source?.user?.name != null"

#############
## Cleanup ##
#############
- lowercase:
      field: network.protocol
      ignore_failure: true
- lowercase:
      field: network.transport
      ignore_failure: true
- lowercase:
      field: event.action
      ignore_failure: true
- remove:
    field:
    - sophos.xg.dst_port
    - sophos.xg.tran_dst_port
    - sophos.xg.recv_bytes
    - sophos.xg.recv_pkts
    - sophos.xg.src_port
    - sophos.xg.tran_src_port
    - sophos.xg.sent_bytes
    - sophos.xg.sent_pkts
    ignore_missing: true
on_failure:
- set:
    field: error.message
    value: '{{ _ingest.on_failure_message }}'
